summaryrefslogtreecommitdiffstats
path: root/pintos-progos
diff options
context:
space:
mode:
Diffstat (limited to 'pintos-progos')
-rw-r--r--pintos-progos/LICENSE95
-rw-r--r--pintos-progos/Make.config52
-rw-r--r--pintos-progos/Makefile29
-rw-r--r--pintos-progos/Makefile.build109
-rw-r--r--pintos-progos/Makefile.kernel20
-rw-r--r--pintos-progos/Makefile.userprog52
-rw-r--r--pintos-progos/devices/block.c223
-rw-r--r--pintos-progos/devices/block.h74
-rw-r--r--pintos-progos/devices/ide.c527
-rw-r--r--pintos-progos/devices/ide.h6
-rw-r--r--pintos-progos/devices/input.c52
-rw-r--r--pintos-progos/devices/input.h12
-rw-r--r--pintos-progos/devices/intq.c114
-rw-r--r--pintos-progos/devices/intq.h43
-rw-r--r--pintos-progos/devices/kbd.c213
-rw-r--r--pintos-progos/devices/kbd.h9
-rw-r--r--pintos-progos/devices/partition.c324
-rw-r--r--pintos-progos/devices/partition.h8
-rw-r--r--pintos-progos/devices/pit.c83
-rw-r--r--pintos-progos/devices/pit.h8
-rw-r--r--pintos-progos/devices/rtc.c112
-rw-r--r--pintos-progos/devices/rtc.h8
-rw-r--r--pintos-progos/devices/serial.c228
-rw-r--r--pintos-progos/devices/serial.h11
-rw-r--r--pintos-progos/devices/shutdown.c131
-rw-r--r--pintos-progos/devices/shutdown.h19
-rw-r--r--pintos-progos/devices/speaker.c68
-rw-r--r--pintos-progos/devices/speaker.h8
-rw-r--r--pintos-progos/devices/timer.c246
-rw-r--r--pintos-progos/devices/timer.h29
-rw-r--r--pintos-progos/devices/vga.c172
-rw-r--r--pintos-progos/devices/vga.h6
-rw-r--r--pintos-progos/examples/.gitignore19
-rw-r--r--pintos-progos/examples/Makefile36
-rw-r--r--pintos-progos/examples/bubsort.c38
-rw-r--r--pintos-progos/examples/cat.c34
-rw-r--r--pintos-progos/examples/cmp.c68
-rw-r--r--pintos-progos/examples/cp.c55
-rw-r--r--pintos-progos/examples/echo.c14
-rw-r--r--pintos-progos/examples/halt.c14
-rw-r--r--pintos-progos/examples/hello.c9
-rw-r--r--pintos-progos/examples/hex-dump.c35
-rw-r--r--pintos-progos/examples/insult.c369
-rw-r--r--pintos-progos/examples/lib/.gitignore1
-rw-r--r--pintos-progos/examples/lib/user/.dummy0
-rw-r--r--pintos-progos/examples/lib/user/.gitignore1
-rw-r--r--pintos-progos/examples/lineup.c46
-rw-r--r--pintos-progos/examples/ls.c90
-rw-r--r--pintos-progos/examples/matmult.c57
-rw-r--r--pintos-progos/examples/mcat.c45
-rw-r--r--pintos-progos/examples/mcp.c68
-rw-r--r--pintos-progos/examples/mkdir.c24
-rw-r--r--pintos-progos/examples/pwd.c152
-rw-r--r--pintos-progos/examples/recursor.c34
-rw-r--r--pintos-progos/examples/rm.c21
-rw-r--r--pintos-progos/examples/shell.c104
-rw-r--r--pintos-progos/examples/test.c101
-rw-r--r--pintos-progos/filesys/.gitignore3
-rw-r--r--pintos-progos/filesys/Make.vars13
-rw-r--r--pintos-progos/filesys/Makefile1
-rw-r--r--pintos-progos/filesys/directory.c236
-rw-r--r--pintos-progos/filesys/directory.h30
-rw-r--r--pintos-progos/filesys/file.c168
-rw-r--r--pintos-progos/filesys/file.h29
-rw-r--r--pintos-progos/filesys/filesys.c103
-rw-r--r--pintos-progos/filesys/filesys.h20
-rw-r--r--pintos-progos/filesys/free-map.c85
-rw-r--r--pintos-progos/filesys/free-map.h17
-rw-r--r--pintos-progos/filesys/fsutil.c222
-rw-r--r--pintos-progos/filesys/fsutil.h10
-rw-r--r--pintos-progos/filesys/inode.c345
-rw-r--r--pintos-progos/filesys/inode.h23
-rw-r--r--pintos-progos/filesys/off_t.h15
-rw-r--r--pintos-progos/intro/Make.vars7
-rw-r--r--pintos-progos/intro/Makefile1
-rw-r--r--pintos-progos/lib/arithmetic.c189
-rw-r--r--pintos-progos/lib/ctype.h28
-rw-r--r--pintos-progos/lib/debug.c32
-rw-r--r--pintos-progos/lib/debug.h39
-rw-r--r--pintos-progos/lib/inttypes.h48
-rw-r--r--pintos-progos/lib/kernel/bitmap.c371
-rw-r--r--pintos-progos/lib/kernel/bitmap.h51
-rw-r--r--pintos-progos/lib/kernel/console.c191
-rw-r--r--pintos-progos/lib/kernel/console.h8
-rw-r--r--pintos-progos/lib/kernel/debug.c123
-rw-r--r--pintos-progos/lib/kernel/hash.c430
-rw-r--r--pintos-progos/lib/kernel/hash.h103
-rw-r--r--pintos-progos/lib/kernel/list.c524
-rw-r--r--pintos-progos/lib/kernel/list.h181
-rw-r--r--pintos-progos/lib/kernel/stdio.h6
-rw-r--r--pintos-progos/lib/limits.h34
-rw-r--r--pintos-progos/lib/packed.h10
-rw-r--r--pintos-progos/lib/random.c83
-rw-r--r--pintos-progos/lib/random.h10
-rw-r--r--pintos-progos/lib/round.h18
-rw-r--r--pintos-progos/lib/stdarg.h14
-rw-r--r--pintos-progos/lib/stdbool.h9
-rw-r--r--pintos-progos/lib/stddef.h12
-rw-r--r--pintos-progos/lib/stdint.h51
-rw-r--r--pintos-progos/lib/stdio.c655
-rw-r--r--pintos-progos/lib/stdio.h40
-rw-r--r--pintos-progos/lib/stdlib.c208
-rw-r--r--pintos-progos/lib/stdlib.h22
-rw-r--r--pintos-progos/lib/string.c375
-rw-r--r--pintos-progos/lib/string.h35
-rw-r--r--pintos-progos/lib/syscall-nr.h34
-rw-r--r--pintos-progos/lib/user/console.c94
-rw-r--r--pintos-progos/lib/user/debug.c25
-rw-r--r--pintos-progos/lib/user/entry.c10
-rw-r--r--pintos-progos/lib/user/stdio.h7
-rw-r--r--pintos-progos/lib/user/syscall.c184
-rw-r--r--pintos-progos/lib/user/syscall.h48
-rw-r--r--pintos-progos/lib/user/user.lds57
-rw-r--r--pintos-progos/lib/ustar.c228
-rw-r--r--pintos-progos/lib/ustar.h29
-rw-r--r--pintos-progos/misc/0001-bochs-2.3.7-jitter.patch78
-rw-r--r--pintos-progos/misc/0002-bochs-2.3.7-triple-fault.patch87
-rw-r--r--pintos-progos/misc/0003-bochs-2.3.7-page-fault-segv.patch93
-rwxr-xr-xpintos-progos/misc/bochs-2.3.7-build.sh42
-rw-r--r--pintos-progos/misc/bochs-2.3.7-gcc43.patch12
-rw-r--r--pintos-progos/misc/bochs-2.3.7-linux3x.patch11
-rw-r--r--pintos-progos/misc/bochs-2.3.7-typos.patch24
-rw-r--r--pintos-progos/misc/gcc-3.3.6-cross-howto39
-rw-r--r--pintos-progos/misc/gdb-macros140
-rw-r--r--pintos-progos/notes/1.txt81
-rw-r--r--pintos-progos/notes/2.txt164
-rw-r--r--pintos-progos/notes/3.txt241
-rw-r--r--pintos-progos/tests/Algorithm/Diff.pm1713
-rw-r--r--pintos-progos/tests/Make.tests76
-rw-r--r--pintos-progos/tests/arc4.c53
-rw-r--r--pintos-progos/tests/arc4.h17
-rw-r--r--pintos-progos/tests/arc4.pm29
-rw-r--r--pintos-progos/tests/cksum.c92
-rw-r--r--pintos-progos/tests/cksum.h8
-rw-r--r--pintos-progos/tests/cksum.pm87
-rw-r--r--pintos-progos/tests/filesys/Grading.no-vm18
-rw-r--r--pintos-progos/tests/filesys/Grading.with-vm22
-rw-r--r--pintos-progos/tests/filesys/base/Make.tests18
-rw-r--r--pintos-progos/tests/filesys/base/Rubric19
-rw-r--r--pintos-progos/tests/filesys/base/child-syn-read.c44
-rw-r--r--pintos-progos/tests/filesys/base/child-syn-wrt.c35
-rw-r--r--pintos-progos/tests/filesys/base/full.inc20
-rw-r--r--pintos-progos/tests/filesys/base/lg-create.c5
-rw-r--r--pintos-progos/tests/filesys/base/lg-create.ck13
-rw-r--r--pintos-progos/tests/filesys/base/lg-full.c6
-rw-r--r--pintos-progos/tests/filesys/base/lg-full.ck16
-rw-r--r--pintos-progos/tests/filesys/base/lg-random.c7
-rw-r--r--pintos-progos/tests/filesys/base/lg-random.ck14
-rw-r--r--pintos-progos/tests/filesys/base/lg-seq-block.c7
-rw-r--r--pintos-progos/tests/filesys/base/lg-seq-block.ck16
-rw-r--r--pintos-progos/tests/filesys/base/lg-seq-random.c6
-rw-r--r--pintos-progos/tests/filesys/base/lg-seq-random.ck16
-rw-r--r--pintos-progos/tests/filesys/base/random.inc59
-rw-r--r--pintos-progos/tests/filesys/base/seq-block.inc20
-rw-r--r--pintos-progos/tests/filesys/base/seq-random.inc22
-rw-r--r--pintos-progos/tests/filesys/base/sm-create.c5
-rw-r--r--pintos-progos/tests/filesys/base/sm-create.ck13
-rw-r--r--pintos-progos/tests/filesys/base/sm-full.c6
-rw-r--r--pintos-progos/tests/filesys/base/sm-full.ck16
-rw-r--r--pintos-progos/tests/filesys/base/sm-random.c7
-rw-r--r--pintos-progos/tests/filesys/base/sm-random.ck14
-rw-r--r--pintos-progos/tests/filesys/base/sm-seq-block.c7
-rw-r--r--pintos-progos/tests/filesys/base/sm-seq-block.ck16
-rw-r--r--pintos-progos/tests/filesys/base/sm-seq-random.c6
-rw-r--r--pintos-progos/tests/filesys/base/sm-seq-random.ck16
-rw-r--r--pintos-progos/tests/filesys/base/syn-read.c31
-rw-r--r--pintos-progos/tests/filesys/base/syn-read.ck33
-rw-r--r--pintos-progos/tests/filesys/base/syn-read.h7
-rw-r--r--pintos-progos/tests/filesys/base/syn-remove.c30
-rw-r--r--pintos-progos/tests/filesys/base/syn-remove.ck16
-rw-r--r--pintos-progos/tests/filesys/base/syn-write.c31
-rw-r--r--pintos-progos/tests/filesys/base/syn-write.ck32
-rw-r--r--pintos-progos/tests/filesys/base/syn-write.h9
-rw-r--r--pintos-progos/tests/filesys/create.inc15
-rw-r--r--pintos-progos/tests/filesys/extended/Make.tests61
-rw-r--r--pintos-progos/tests/filesys/extended/Rubric.functionality26
-rw-r--r--pintos-progos/tests/filesys/extended/Rubric.persistence24
-rw-r--r--pintos-progos/tests/filesys/extended/Rubric.robustness9
-rw-r--r--pintos-progos/tests/filesys/extended/child-syn-rw.c53
-rw-r--r--pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-empty-name.c12
-rw-r--r--pintos-progos/tests/filesys/extended/dir-empty-name.ck10
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck16
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mk-tree.c12
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mk-tree.ck12
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mkdir.c15
-rw-r--r--pintos-progos/tests/filesys/extended/dir-mkdir.ck13
-rw-r--r--pintos-progos/tests/filesys/extended/dir-open-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-open.c21
-rw-r--r--pintos-progos/tests/filesys/extended/dir-open.ck20
-rw-r--r--pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-over-file.c13
-rw-r--r--pintos-progos/tests/filesys/extended/dir-over-file.ck11
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck8
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-cwd.c75
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-cwd.ck51
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-parent.c16
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-parent.ck14
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-root.c13
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-root.ck11
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-tree.c62
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rm-tree.ck14
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rmdir.c14
-rw-r--r--pintos-progos/tests/filesys/extended/dir-rmdir.ck12
-rw-r--r--pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/dir-under-file.c13
-rw-r--r--pintos-progos/tests/filesys/extended/dir-under-file.ck11
-rw-r--r--pintos-progos/tests/filesys/extended/dir-vine-persistence.ck37
-rw-r--r--pintos-progos/tests/filesys/extended/dir-vine.c85
-rw-r--r--pintos-progos/tests/filesys/extended/dir-vine.ck11
-rw-r--r--pintos-progos/tests/filesys/extended/grow-create-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/grow-create.c4
-rw-r--r--pintos-progos/tests/filesys/extended/grow-create.ck13
-rw-r--r--pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck9
-rw-r--r--pintos-progos/tests/filesys/extended/grow-dir-lg.c6
-rw-r--r--pintos-progos/tests/filesys/extended/grow-dir-lg.ck61
-rw-r--r--pintos-progos/tests/filesys/extended/grow-dir.inc41
-rw-r--r--pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck7
-rw-r--r--pintos-progos/tests/filesys/extended/grow-file-size.c33
-rw-r--r--pintos-progos/tests/filesys/extended/grow-file-size.ck17
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck9
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-lg.c4
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-lg.ck60
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck9
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-sm.c4
-rw-r--r--pintos-progos/tests/filesys/extended/grow-root-sm.ck30
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck7
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-lg.c5
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-lg.ck17
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck7
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-sm.c5
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq-sm.ck17
-rw-r--r--pintos-progos/tests/filesys/extended/grow-seq.inc20
-rw-r--r--pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck6
-rw-r--r--pintos-progos/tests/filesys/extended/grow-sparse.c25
-rw-r--r--pintos-progos/tests/filesys/extended/grow-sparse.ck17
-rw-r--r--pintos-progos/tests/filesys/extended/grow-tell-persistence.ck7
-rw-r--r--pintos-progos/tests/filesys/extended/grow-tell.c32
-rw-r--r--pintos-progos/tests/filesys/extended/grow-tell.ck17
-rw-r--r--pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck9
-rw-r--r--pintos-progos/tests/filesys/extended/grow-two-files.c62
-rw-r--r--pintos-progos/tests/filesys/extended/grow-two-files.ck23
-rw-r--r--pintos-progos/tests/filesys/extended/mk-tree.c67
-rw-r--r--pintos-progos/tests/filesys/extended/mk-tree.h6
-rw-r--r--pintos-progos/tests/filesys/extended/syn-rw-persistence.ck8
-rw-r--r--pintos-progos/tests/filesys/extended/syn-rw.c35
-rw-r--r--pintos-progos/tests/filesys/extended/syn-rw.ck20
-rw-r--r--pintos-progos/tests/filesys/extended/syn-rw.h9
-rw-r--r--pintos-progos/tests/filesys/extended/tar.c208
-rw-r--r--pintos-progos/tests/filesys/seq-test.c37
-rw-r--r--pintos-progos/tests/filesys/seq-test.h11
-rw-r--r--pintos-progos/tests/internal/list.c174
-rw-r--r--pintos-progos/tests/internal/stdio.c208
-rw-r--r--pintos-progos/tests/internal/stdlib.c114
-rw-r--r--pintos-progos/tests/intro/Grading5
-rw-r--r--pintos-progos/tests/intro/alarm-clock/Make.tests15
-rw-r--r--pintos-progos/tests/intro/alarm-clock/Rubric7
l---------pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck1
l---------pintos-progos/tests/intro/alarm-clock/alarm-negative.c1
l---------pintos-progos/tests/intro/alarm-clock/alarm-negative.ck1
l---------pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c1
l---------pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck1
l---------pintos-progos/tests/intro/alarm-clock/alarm-single.ck1
l---------pintos-progos/tests/intro/alarm-clock/alarm-wait.c1
l---------pintos-progos/tests/intro/alarm-clock/alarm-zero.c1
l---------pintos-progos/tests/intro/alarm-clock/alarm-zero.ck1
-rw-r--r--pintos-progos/tests/intro/alarm-clock/tests.c80
-rw-r--r--pintos-progos/tests/intro/userprog-args/Make.tests27
-rw-r--r--pintos-progos/tests/intro/userprog-args/Rubric7
l---------pintos-progos/tests/intro/userprog-args/args-dbl-space.ck1
-rw-r--r--pintos-progos/tests/intro/userprog-args/args-limit.c70
-rw-r--r--pintos-progos/tests/intro/userprog-args/args-limit.ck11
l---------pintos-progos/tests/intro/userprog-args/args-many.ck1
l---------pintos-progos/tests/intro/userprog-args/args-multiple.ck1
l---------pintos-progos/tests/intro/userprog-args/args-none.ck1
l---------pintos-progos/tests/intro/userprog-args/args-single.ck1
l---------pintos-progos/tests/intro/userprog-args/args.c1
l---------pintos-progos/tests/intro/userprog-args/child-simple.c1
-rw-r--r--pintos-progos/tests/lib.c196
-rw-r--r--pintos-progos/tests/lib.h50
-rw-r--r--pintos-progos/tests/lib.pm19
-rw-r--r--pintos-progos/tests/main.c15
-rw-r--r--pintos-progos/tests/main.h6
-rwxr-xr-xpintos-progos/tests/make-grade152
-rw-r--r--pintos-progos/tests/random.pm27
-rw-r--r--pintos-progos/tests/tests.pm625
-rw-r--r--pintos-progos/tests/threads/Grading11
-rw-r--r--pintos-progos/tests/threads/Make.tests56
-rw-r--r--pintos-progos/tests/threads/Rubric.alarm8
-rw-r--r--pintos-progos/tests/threads/Rubric.mlfqs14
-rw-r--r--pintos-progos/tests/threads/Rubric.priority15
-rw-r--r--pintos-progos/tests/threads/alarm-multiple.ck4
-rw-r--r--pintos-progos/tests/threads/alarm-negative.c15
-rw-r--r--pintos-progos/tests/threads/alarm-negative.ck10
-rw-r--r--pintos-progos/tests/threads/alarm-priority.c58
-rw-r--r--pintos-progos/tests/threads/alarm-priority.ck19
-rw-r--r--pintos-progos/tests/threads/alarm-simultaneous.c94
-rw-r--r--pintos-progos/tests/threads/alarm-simultaneous.ck27
-rw-r--r--pintos-progos/tests/threads/alarm-single.ck4
-rw-r--r--pintos-progos/tests/threads/alarm-wait.c152
-rw-r--r--pintos-progos/tests/threads/alarm-zero.c15
-rw-r--r--pintos-progos/tests/threads/alarm-zero.ck10
-rw-r--r--pintos-progos/tests/threads/alarm.pm32
-rw-r--r--pintos-progos/tests/threads/mlfqs-block.c64
-rw-r--r--pintos-progos/tests/threads/mlfqs-block.ck17
-rw-r--r--pintos-progos/tests/threads/mlfqs-fair-2.ck7
-rw-r--r--pintos-progos/tests/threads/mlfqs-fair-20.ck7
-rw-r--r--pintos-progos/tests/threads/mlfqs-fair.c124
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-1.c60
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-1.ck15
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-60.c155
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-60.ck36
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-avg.c167
-rw-r--r--pintos-progos/tests/threads/mlfqs-load-avg.ck36
-rw-r--r--pintos-progos/tests/threads/mlfqs-nice-10.ck7
-rw-r--r--pintos-progos/tests/threads/mlfqs-nice-2.ck7
-rw-r--r--pintos-progos/tests/threads/mlfqs-recent-1.c144
-rw-r--r--pintos-progos/tests/threads/mlfqs-recent-1.ck31
-rw-r--r--pintos-progos/tests/threads/mlfqs.pm146
-rw-r--r--pintos-progos/tests/threads/priority-change.c31
-rw-r--r--pintos-progos/tests/threads/priority-change.ck14
-rw-r--r--pintos-progos/tests/threads/priority-condvar.c53
-rw-r--r--pintos-progos/tests/threads/priority-condvar.ck39
-rw-r--r--pintos-progos/tests/threads/priority-donate-chain.c114
-rw-r--r--pintos-progos/tests/threads/priority-donate-chain.ck46
-rw-r--r--pintos-progos/tests/threads/priority-donate-lower.c51
-rw-r--r--pintos-progos/tests/threads/priority-donate-lower.ck16
-rw-r--r--pintos-progos/tests/threads/priority-donate-multiple.c77
-rw-r--r--pintos-progos/tests/threads/priority-donate-multiple.ck19
-rw-r--r--pintos-progos/tests/threads/priority-donate-multiple2.c90
-rw-r--r--pintos-progos/tests/threads/priority-donate-multiple2.ck19
-rw-r--r--pintos-progos/tests/threads/priority-donate-nest.c94
-rw-r--r--pintos-progos/tests/threads/priority-donate-nest.ck19
-rw-r--r--pintos-progos/tests/threads/priority-donate-one.c65
-rw-r--r--pintos-progos/tests/threads/priority-donate-one.ck17
-rw-r--r--pintos-progos/tests/threads/priority-donate-sema.c82
-rw-r--r--pintos-progos/tests/threads/priority-donate-sema.ck16
-rw-r--r--pintos-progos/tests/threads/priority-fifo.c99
-rw-r--r--pintos-progos/tests/threads/priority-fifo.ck63
-rw-r--r--pintos-progos/tests/threads/priority-preempt.c41
-rw-r--r--pintos-progos/tests/threads/priority-preempt.ck16
-rw-r--r--pintos-progos/tests/threads/priority-sema.c45
-rw-r--r--pintos-progos/tests/threads/priority-sema.ck29
-rw-r--r--pintos-progos/tests/threads/tests.c102
-rw-r--r--pintos-progos/tests/threads/tests.h41
-rw-r--r--pintos-progos/tests/userprog/Make.tests133
-rw-r--r--pintos-progos/tests/userprog/Rubric.functionality52
-rw-r--r--pintos-progos/tests/userprog/Rubric.robustness48
-rw-r--r--pintos-progos/tests/userprog/args-dbl-space.ck15
-rw-r--r--pintos-progos/tests/userprog/args-many.ck35
-rw-r--r--pintos-progos/tests/userprog/args-multiple.ck17
-rw-r--r--pintos-progos/tests/userprog/args-none.ck13
-rw-r--r--pintos-progos/tests/userprog/args-single.ck14
-rw-r--r--pintos-progos/tests/userprog/args.c25
-rw-r--r--pintos-progos/tests/userprog/bad-jump.c13
-rw-r--r--pintos-progos/tests/userprog/bad-jump.ck9
-rw-r--r--pintos-progos/tests/userprog/bad-jump2.c13
-rw-r--r--pintos-progos/tests/userprog/bad-jump2.ck9
-rw-r--r--pintos-progos/tests/userprog/bad-read.c13
-rw-r--r--pintos-progos/tests/userprog/bad-read.ck9
-rw-r--r--pintos-progos/tests/userprog/bad-read2.c13
-rw-r--r--pintos-progos/tests/userprog/bad-read2.ck9
-rw-r--r--pintos-progos/tests/userprog/bad-write.c12
-rw-r--r--pintos-progos/tests/userprog/bad-write.ck9
-rw-r--r--pintos-progos/tests/userprog/bad-write2.c12
-rw-r--r--pintos-progos/tests/userprog/bad-write2.ck9
-rw-r--r--pintos-progos/tests/userprog/boundary.c33
-rw-r--r--pintos-progos/tests/userprog/boundary.h7
-rw-r--r--pintos-progos/tests/userprog/child-bad.c14
-rw-r--r--pintos-progos/tests/userprog/child-close.c28
-rw-r--r--pintos-progos/tests/userprog/child-rox.c55
-rw-r--r--pintos-progos/tests/userprog/child-simple.c15
-rw-r--r--pintos-progos/tests/userprog/close-bad-fd.c11
-rw-r--r--pintos-progos/tests/userprog/close-bad-fd.ck13
-rw-r--r--pintos-progos/tests/userprog/close-normal.c14
-rw-r--r--pintos-progos/tests/userprog/close-normal.ck12
-rw-r--r--pintos-progos/tests/userprog/close-stdin.c11
-rw-r--r--pintos-progos/tests/userprog/close-stdin.ck13
-rw-r--r--pintos-progos/tests/userprog/close-stdout.c11
-rw-r--r--pintos-progos/tests/userprog/close-stdout.ck13
-rw-r--r--pintos-progos/tests/userprog/close-twice.c18
-rw-r--r--pintos-progos/tests/userprog/close-twice.ck19
-rw-r--r--pintos-progos/tests/userprog/create-bad-ptr.c12
-rw-r--r--pintos-progos/tests/userprog/create-bad-ptr.ck9
-rw-r--r--pintos-progos/tests/userprog/create-bound.c14
-rw-r--r--pintos-progos/tests/userprog/create-bound.ck11
-rw-r--r--pintos-progos/tests/userprog/create-empty.c10
-rw-r--r--pintos-progos/tests/userprog/create-empty.ck14
-rw-r--r--pintos-progos/tests/userprog/create-exists.c16
-rw-r--r--pintos-progos/tests/userprog/create-exists.ck15
-rw-r--r--pintos-progos/tests/userprog/create-long.c17
-rw-r--r--pintos-progos/tests/userprog/create-long.ck11
-rw-r--r--pintos-progos/tests/userprog/create-normal.c10
-rw-r--r--pintos-progos/tests/userprog/create-normal.ck11
-rw-r--r--pintos-progos/tests/userprog/create-null.c11
-rw-r--r--pintos-progos/tests/userprog/create-null.ck9
-rw-r--r--pintos-progos/tests/userprog/exec-arg.c10
-rw-r--r--pintos-progos/tests/userprog/exec-arg.ck17
-rw-r--r--pintos-progos/tests/userprog/exec-bad-ptr.c11
-rw-r--r--pintos-progos/tests/userprog/exec-bad-ptr.ck13
-rw-r--r--pintos-progos/tests/userprog/exec-missing.c12
-rw-r--r--pintos-progos/tests/userprog/exec-missing.ck31
-rw-r--r--pintos-progos/tests/userprog/exec-multiple.c14
-rw-r--r--pintos-progos/tests/userprog/exec-multiple.ck18
-rw-r--r--pintos-progos/tests/userprog/exec-once.c11
-rw-r--r--pintos-progos/tests/userprog/exec-once.ck12
-rw-r--r--pintos-progos/tests/userprog/exit.c11
-rw-r--r--pintos-progos/tests/userprog/exit.ck9
-rw-r--r--pintos-progos/tests/userprog/halt.c11
-rw-r--r--pintos-progos/tests/userprog/halt.ck15
-rw-r--r--pintos-progos/tests/userprog/lib/.gitignore1
-rw-r--r--pintos-progos/tests/userprog/lib/user/.dummy0
-rw-r--r--pintos-progos/tests/userprog/lib/user/.gitignore1
-rw-r--r--pintos-progos/tests/userprog/multi-child-fd.c25
-rw-r--r--pintos-progos/tests/userprog/multi-child-fd.ck25
-rw-r--r--pintos-progos/tests/userprog/multi-recurse.c34
-rw-r--r--pintos-progos/tests/userprog/multi-recurse.ck70
-rw-r--r--pintos-progos/tests/userprog/no-vm/Make.tests8
-rw-r--r--pintos-progos/tests/userprog/no-vm/Rubric3
-rw-r--r--pintos-progos/tests/userprog/no-vm/multi-oom.c179
-rw-r--r--pintos-progos/tests/userprog/no-vm/multi-oom.ck10
-rw-r--r--pintos-progos/tests/userprog/null.ck8
-rw-r--r--pintos-progos/tests/userprog/open-bad-ptr.c13
-rw-r--r--pintos-progos/tests/userprog/open-bad-ptr.ck13
-rw-r--r--pintos-progos/tests/userprog/open-boundary.c14
-rw-r--r--pintos-progos/tests/userprog/open-boundary.ck11
-rw-r--r--pintos-progos/tests/userprog/open-empty.c13
-rw-r--r--pintos-progos/tests/userprog/open-empty.ck10
-rw-r--r--pintos-progos/tests/userprog/open-missing.c13
-rw-r--r--pintos-progos/tests/userprog/open-missing.ck10
-rw-r--r--pintos-progos/tests/userprog/open-normal.c13
-rw-r--r--pintos-progos/tests/userprog/open-normal.ck10
-rw-r--r--pintos-progos/tests/userprog/open-null.c12
-rw-r--r--pintos-progos/tests/userprog/open-null.ck13
-rw-r--r--pintos-progos/tests/userprog/open-twice.c19
-rw-r--r--pintos-progos/tests/userprog/open-twice.ck12
-rw-r--r--pintos-progos/tests/userprog/read-bad-fd.c21
-rw-r--r--pintos-progos/tests/userprog/read-bad-fd.ck13
-rw-r--r--pintos-progos/tests/userprog/read-bad-ptr.c16
-rw-r--r--pintos-progos/tests/userprog/read-bad-ptr.ck15
-rw-r--r--pintos-progos/tests/userprog/read-boundary.c30
-rw-r--r--pintos-progos/tests/userprog/read-boundary.ck11
-rw-r--r--pintos-progos/tests/userprog/read-normal.c11
-rw-r--r--pintos-progos/tests/userprog/read-normal.ck13
-rw-r--r--pintos-progos/tests/userprog/read-stdout.c14
-rw-r--r--pintos-progos/tests/userprog/read-stdout.ck13
-rw-r--r--pintos-progos/tests/userprog/read-zero.c22
-rw-r--r--pintos-progos/tests/userprog/read-zero.ck11
-rw-r--r--pintos-progos/tests/userprog/rox-child.c5
-rw-r--r--pintos-progos/tests/userprog/rox-child.ck20
-rw-r--r--pintos-progos/tests/userprog/rox-child.inc33
-rw-r--r--pintos-progos/tests/userprog/rox-multichild.c5
-rw-r--r--pintos-progos/tests/userprog/rox-multichild.ck44
-rw-r--r--pintos-progos/tests/userprog/rox-simple.c19
-rw-r--r--pintos-progos/tests/userprog/rox-simple.ck13
-rw-r--r--pintos-progos/tests/userprog/sample.inc6
-rw-r--r--pintos-progos/tests/userprog/sample.txt4
-rw-r--r--pintos-progos/tests/userprog/sc-bad-arg.c17
-rw-r--r--pintos-progos/tests/userprog/sc-bad-arg.ck9
-rw-r--r--pintos-progos/tests/userprog/sc-bad-sp.c20
-rw-r--r--pintos-progos/tests/userprog/sc-bad-sp.ck9
-rw-r--r--pintos-progos/tests/userprog/sc-boundary-2.c22
-rw-r--r--pintos-progos/tests/userprog/sc-boundary-2.ck9
-rw-r--r--pintos-progos/tests/userprog/sc-boundary.c22
-rw-r--r--pintos-progos/tests/userprog/sc-boundary.ck9
-rw-r--r--pintos-progos/tests/userprog/wait-bad-pid.c11
-rw-r--r--pintos-progos/tests/userprog/wait-bad-pid.ck13
-rw-r--r--pintos-progos/tests/userprog/wait-killed.c11
-rw-r--r--pintos-progos/tests/userprog/wait-killed.ck13
-rw-r--r--pintos-progos/tests/userprog/wait-simple.c11
-rw-r--r--pintos-progos/tests/userprog/wait-simple.ck13
-rw-r--r--pintos-progos/tests/userprog/wait-twice.c15
-rw-r--r--pintos-progos/tests/userprog/wait-twice.ck14
-rw-r--r--pintos-progos/tests/userprog/write-bad-fd.c20
-rw-r--r--pintos-progos/tests/userprog/write-bad-fd.ck13
-rw-r--r--pintos-progos/tests/userprog/write-bad-ptr.c16
-rw-r--r--pintos-progos/tests/userprog/write-bad-ptr.ck15
-rw-r--r--pintos-progos/tests/userprog/write-boundary.c25
-rw-r--r--pintos-progos/tests/userprog/write-boundary.ck11
-rw-r--r--pintos-progos/tests/userprog/write-normal.c20
-rw-r--r--pintos-progos/tests/userprog/write-normal.ck12
-rw-r--r--pintos-progos/tests/userprog/write-stdin.c14
-rw-r--r--pintos-progos/tests/userprog/write-stdin.ck13
-rw-r--r--pintos-progos/tests/userprog/write-zero.c20
-rw-r--r--pintos-progos/tests/userprog/write-zero.ck11
-rw-r--r--pintos-progos/tests/vm/Grading12
-rw-r--r--pintos-progos/tests/vm/Make.tests107
-rw-r--r--pintos-progos/tests/vm/Rubric.functionality21
-rw-r--r--pintos-progos/tests/vm/Rubric.paging8
-rw-r--r--pintos-progos/tests/vm/Rubric.robustness21
-rw-r--r--pintos-progos/tests/vm/child-inherit.c16
-rw-r--r--pintos-progos/tests/vm/child-linear.c36
-rw-r--r--pintos-progos/tests/vm/child-mm-wrt.c24
-rw-r--r--pintos-progos/tests/vm/child-qsort-mm.c25
-rw-r--r--pintos-progos/tests/vm/child-qsort.c32
-rw-r--r--pintos-progos/tests/vm/child-sort.c42
-rw-r--r--pintos-progos/tests/vm/mmap-bad-fd.c15
-rw-r--r--pintos-progos/tests/vm/mmap-bad-fd.ck15
-rw-r--r--pintos-progos/tests/vm/mmap-clean.c53
-rw-r--r--pintos-progos/tests/vm/mmap-clean.ck16
-rw-r--r--pintos-progos/tests/vm/mmap-close.c27
-rw-r--r--pintos-progos/tests/vm/mmap-close.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-exit.c22
-rw-r--r--pintos-progos/tests/vm/mmap-exit.ck17
-rw-r--r--pintos-progos/tests/vm/mmap-inherit.c32
-rw-r--r--pintos-progos/tests/vm/mmap-inherit.ck16
-rw-r--r--pintos-progos/tests/vm/mmap-lazy-seq.c52
-rw-r--r--pintos-progos/tests/vm/mmap-lazy-seq.ck27
-rw-r--r--pintos-progos/tests/vm/mmap-misalign.c16
-rw-r--r--pintos-progos/tests/vm/mmap-misalign.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-null.c15
-rw-r--r--pintos-progos/tests/vm/mmap-null.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-over-code.c19
-rw-r--r--pintos-progos/tests/vm/mmap-over-code.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-over-data.c21
-rw-r--r--pintos-progos/tests/vm/mmap-over-data.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-over-stk.c19
-rw-r--r--pintos-progos/tests/vm/mmap-over-stk.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-overlap.c20
-rw-r--r--pintos-progos/tests/vm/mmap-overlap.ck13
-rw-r--r--pintos-progos/tests/vm/mmap-read.c32
-rw-r--r--pintos-progos/tests/vm/mmap-read.ck11
-rw-r--r--pintos-progos/tests/vm/mmap-remove.c43
-rw-r--r--pintos-progos/tests/vm/mmap-remove.ck14
-rw-r--r--pintos-progos/tests/vm/mmap-shuffle.c38
-rw-r--r--pintos-progos/tests/vm/mmap-shuffle.ck47
-rw-r--r--pintos-progos/tests/vm/mmap-twice.c28
-rw-r--r--pintos-progos/tests/vm/mmap-twice.ck15
-rw-r--r--pintos-progos/tests/vm/mmap-unmap.c23
-rw-r--r--pintos-progos/tests/vm/mmap-unmap.ck7
-rw-r--r--pintos-progos/tests/vm/mmap-write.c32
-rw-r--r--pintos-progos/tests/vm/mmap-write.ck13
-rw-r--r--pintos-progos/tests/vm/mmap-zero.c27
-rw-r--r--pintos-progos/tests/vm/mmap-zero.ck12
-rw-r--r--pintos-progos/tests/vm/page-linear.c44
-rw-r--r--pintos-progos/tests/vm/page-linear.ck14
-rw-r--r--pintos-progos/tests/vm/page-merge-mm.c8
-rw-r--r--pintos-progos/tests/vm/page-merge-mm.ck29
-rw-r--r--pintos-progos/tests/vm/page-merge-par.c8
-rw-r--r--pintos-progos/tests/vm/page-merge-par.ck29
-rw-r--r--pintos-progos/tests/vm/page-merge-seq.c137
-rw-r--r--pintos-progos/tests/vm/page-merge-seq.ck29
-rw-r--r--pintos-progos/tests/vm/page-merge-stk.c8
-rw-r--r--pintos-progos/tests/vm/page-merge-stk.ck29
-rw-r--r--pintos-progos/tests/vm/page-parallel.c21
-rw-r--r--pintos-progos/tests/vm/page-parallel.ck17
-rw-r--r--pintos-progos/tests/vm/page-shuffle.c30
-rw-r--r--pintos-progos/tests/vm/page-shuffle.ck44
-rw-r--r--pintos-progos/tests/vm/parallel-merge.c149
-rw-r--r--pintos-progos/tests/vm/parallel-merge.h6
-rw-r--r--pintos-progos/tests/vm/process_death.pm22
-rw-r--r--pintos-progos/tests/vm/pt-bad-addr.c11
-rw-r--r--pintos-progos/tests/vm/pt-bad-addr.ck7
-rw-r--r--pintos-progos/tests/vm/pt-bad-read.c16
-rw-r--r--pintos-progos/tests/vm/pt-bad-read.ck10
-rw-r--r--pintos-progos/tests/vm/pt-big-stk-obj.c20
-rw-r--r--pintos-progos/tests/vm/pt-big-stk-obj.ck10
-rw-r--r--pintos-progos/tests/vm/pt-grow-bad.c14
-rw-r--r--pintos-progos/tests/vm/pt-grow-bad.ck9
-rw-r--r--pintos-progos/tests/vm/pt-grow-pusha.c20
-rw-r--r--pintos-progos/tests/vm/pt-grow-pusha.ck9
-rw-r--r--pintos-progos/tests/vm/pt-grow-stack.c20
-rw-r--r--pintos-progos/tests/vm/pt-grow-stack.ck10
-rw-r--r--pintos-progos/tests/vm/pt-grow-stk-sc.c32
-rw-r--r--pintos-progos/tests/vm/pt-grow-stk-sc.ck15
-rw-r--r--pintos-progos/tests/vm/pt-write-code-2.c15
-rw-r--r--pintos-progos/tests/vm/pt-write-code.c12
-rw-r--r--pintos-progos/tests/vm/pt-write-code.ck7
-rw-r--r--pintos-progos/tests/vm/pt-write-code2.ck10
-rw-r--r--pintos-progos/tests/vm/qsort.c136
-rw-r--r--pintos-progos/tests/vm/qsort.h8
-rw-r--r--pintos-progos/tests/vm/sample.inc19
-rw-r--r--pintos-progos/tests/vm/sample.txt17
-rw-r--r--pintos-progos/threads/.gitignore3
-rw-r--r--pintos-progos/threads/Make.vars7
-rw-r--r--pintos-progos/threads/Makefile1
-rw-r--r--pintos-progos/threads/flags.h8
-rw-r--r--pintos-progos/threads/init.c453
-rw-r--r--pintos-progos/threads/init.h12
-rw-r--r--pintos-progos/threads/interrupt.c438
-rw-r--r--pintos-progos/threads/interrupt.h70
-rw-r--r--pintos-progos/threads/intr-stubs.S203
-rw-r--r--pintos-progos/threads/intr-stubs.h19
-rw-r--r--pintos-progos/threads/io.h115
-rw-r--r--pintos-progos/threads/kernel.lds.S30
-rw-r--r--pintos-progos/threads/loader.S263
-rw-r--r--pintos-progos/threads/loader.h40
-rw-r--r--pintos-progos/threads/malloc.c294
-rw-r--r--pintos-progos/threads/malloc.h13
-rw-r--r--pintos-progos/threads/palloc.c199
-rw-r--r--pintos-progos/threads/palloc.h20
-rw-r--r--pintos-progos/threads/pte.h107
-rw-r--r--pintos-progos/threads/start.S204
-rw-r--r--pintos-progos/threads/switch.S65
-rw-r--r--pintos-progos/threads/switch.h39
-rw-r--r--pintos-progos/threads/synch.c338
-rw-r--r--pintos-progos/threads/synch.h51
-rw-r--r--pintos-progos/threads/thread.c594
-rw-r--r--pintos-progos/threads/thread.h144
-rw-r--r--pintos-progos/threads/vaddr.h89
-rw-r--r--pintos-progos/userprog/.gitignore3
-rw-r--r--pintos-progos/userprog/Make.vars7
-rw-r--r--pintos-progos/userprog/Makefile1
-rw-r--r--pintos-progos/userprog/exception.c174
-rw-r--r--pintos-progos/userprog/exception.h12
-rw-r--r--pintos-progos/userprog/gdt.c146
-rw-r--r--pintos-progos/userprog/gdt.h15
-rw-r--r--pintos-progos/userprog/pagedir.c263
-rw-r--r--pintos-progos/userprog/pagedir.h18
-rw-r--r--pintos-progos/userprog/process.c721
-rw-r--r--pintos-progos/userprog/process.h47
-rw-r--r--pintos-progos/userprog/syscall.c563
-rw-r--r--pintos-progos/userprog/syscall.h5
-rw-r--r--pintos-progos/userprog/tss.c106
-rw-r--r--pintos-progos/userprog/tss.h11
-rw-r--r--pintos-progos/utils/.gitignore3
-rw-r--r--pintos-progos/utils/Makefile11
-rw-r--r--pintos-progos/utils/Pintos.pm491
-rwxr-xr-xpintos-progos/utils/backtrace106
-rwxr-xr-xpintos-progos/utils/pintos955
-rwxr-xr-xpintos-progos/utils/pintos-gdb21
-rwxr-xr-xpintos-progos/utils/pintos-mkdisk134
-rw-r--r--pintos-progos/utils/pintos-set-cmdline42
-rw-r--r--pintos-progos/utils/setitimer-helper.c49
-rw-r--r--pintos-progos/utils/squish-pty.c355
-rw-r--r--pintos-progos/utils/squish-unix.c338
-rw-r--r--pintos-progos/vm/.gitignore3
-rw-r--r--pintos-progos/vm/Make.vars7
-rw-r--r--pintos-progos/vm/Makefile1
634 files changed, 33824 insertions, 0 deletions
diff --git a/pintos-progos/LICENSE b/pintos-progos/LICENSE
new file mode 100644
index 0000000..8702541
--- /dev/null
+++ b/pintos-progos/LICENSE
@@ -0,0 +1,95 @@
1Pintos, including its documentation, is subject to the following
2license:
3
4 Copyright (C) 2004, 2005, 2006 Board of Trustees, Leland Stanford
5 Jr. University. All rights reserved.
6
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25
26A few individual files in Pintos were originally derived from other
27projects, but they have been extensively modified for use in Pintos.
28The original code falls under the original license, and modifications
29for Pintos are additionally covered by the Pintos license above.
30
31In particular, code derived from Nachos is subject to the following
32license:
33
34/* Copyright (c) 1992-1996 The Regents of the University of California.
35 All rights reserved.
36
37 Permission to use, copy, modify, and distribute this software
38 and its documentation for any purpose, without fee, and
39 without written agreement is hereby granted, provided that the
40 above copyright notice and the following two paragraphs appear
41 in all copies of this software.
42
43 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO
44 ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
45 CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE
46 AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA
47 HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
48
49 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
50 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
51 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
52 PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
53 BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
54 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
55 MODIFICATIONS.
56*/
57
58Also, code derived from MIT's 6.828 course code is subject to the
59following license:
60
61/*
62 * Copyright (C) 1997 Massachusetts Institute of Technology
63 *
64 * This software is being provided by the copyright holders under the
65 * following license. By obtaining, using and/or copying this software,
66 * you agree that you have read, understood, and will comply with the
67 * following terms and conditions:
68 *
69 * Permission to use, copy, modify, distribute, and sell this software
70 * and its documentation for any purpose and without fee or royalty is
71 * hereby granted, provided that the full text of this NOTICE appears on
72 * ALL copies of the software and documentation or portions thereof,
73 * including modifications, that you make.
74 *
75 * THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
76 * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
77 * BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
78 * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
79 * THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
80 * THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
81 * HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
82 * DOCUMENTATION.
83 *
84 * The name and trademarks of copyright holders may NOT be used in
85 * advertising or publicity pertaining to the software without specific,
86 * written prior permission. Title to copyright in this software and any
87 * associated documentation will at all times remain with copyright
88 * holders. See the file AUTHORS which should have accompanied this software
89 * for a list of all copyright holders.
90 *
91 * This file may be derived from previously copyrighted software. This
92 * copyright applies only to those changes made by the copyright
93 * holders listed in the AUTHORS file. The rest of this file is covered by
94 * the copyright notices, if any, listed below.
95 */
diff --git a/pintos-progos/Make.config b/pintos-progos/Make.config
new file mode 100644
index 0000000..f00e8fc
--- /dev/null
+++ b/pintos-progos/Make.config
@@ -0,0 +1,52 @@
1# -*- makefile -*-
2
3SHELL = /bin/sh
4
5VPATH = $(SRCDIR)
6
7# Binary utilities.
8# If the host appears to be x86, use the normal tools.
9# If it's x86-64, use the compiler and linker in 32-bit mode.
10# Otherwise assume cross-tools are installed as i386-elf-*.
11X86 = i.86\|pentium.*\|[pk][56]\|nexgen\|viac3\|6x86\|athlon.*\|i86pc
12X86_64 = x86_64
13ifneq (0, $(shell expr `uname -m` : '$(X86)'))
14 CC = gcc
15 LD = ld
16 OBJCOPY = objcopy
17else
18 ifneq (0, $(shell expr `uname -m` : '$(X86_64)'))
19 CC = gcc -m32
20 LD = ld -melf_i386
21 OBJCOPY = objcopy
22 else
23 CC = i386-elf-gcc
24 LD = i386-elf-ld
25 OBJCOPY = i386-elf-objcopy
26 endif
27endif
28
29ifeq ($(strip $(shell command -v $(CC) 2> /dev/null)),)
30$(warning *** Compiler ($(CC)) not found. Did you set $$PATH properly? Please refer to the Getting Started section in the documentation for details. ***)
31endif
32
33# Compiler and assembler invocation.
34DEFINES =
35WARNINGS = -Wall -W -Wstrict-prototypes -Wmissing-prototypes -Wsystem-headers
36CFLAGS = -g -msoft-float -O
37CPPFLAGS = -nostdinc -I$(SRCDIR) -I$(SRCDIR)/lib
38ASFLAGS = -Wa,--gstabs
39LDFLAGS =
40DEPS = -MMD -MF $(@:.o=.d)
41
42# Turn off -fstack-protector, which we don't support.
43CFLAGS += -fno-stack-protector
44
45# Turn off --build-id in the linker, which confuses the Pintos loader.
46LDFLAGS += -Wl,--build-id=none
47
48%.o: %.c
49 $(CC) -c $< -o $@ $(CFLAGS) $(CPPFLAGS) $(WARNINGS) $(DEFINES) $(DEPS)
50
51%.o: %.S
52 $(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES) $(DEPS)
diff --git a/pintos-progos/Makefile b/pintos-progos/Makefile
new file mode 100644
index 0000000..3ba9194
--- /dev/null
+++ b/pintos-progos/Makefile
@@ -0,0 +1,29 @@
1BUILD_SUBDIRS = threads userprog vm filesys intro
2
3all::
4 @echo "Run 'make' in subdirectories: $(BUILD_SUBDIRS)."
5 @echo "This top-level make has only 'clean' targets."
6
7CLEAN_SUBDIRS = $(BUILD_SUBDIRS) examples utils
8
9clean::
10 for d in $(CLEAN_SUBDIRS); do $(MAKE) -C $$d $@; done
11 rm -f TAGS tags
12
13distclean:: clean
14 find . -name '*~' -exec rm '{}' \;
15
16TAGS_SUBDIRS = $(BUILD_SUBDIRS) devices lib
17TAGS_SOURCES = find $(TAGS_SUBDIRS) -name \*.[chS] -print
18
19TAGS::
20 etags --members `$(TAGS_SOURCES)`
21
22tags::
23 ctags -T --no-warn `$(TAGS_SOURCES)`
24
25cscope.files::
26 $(TAGS_SOURCES) > cscope.files
27
28cscope:: cscope.files
29 cscope -b -q -k
diff --git a/pintos-progos/Makefile.build b/pintos-progos/Makefile.build
new file mode 100644
index 0000000..e997d27
--- /dev/null
+++ b/pintos-progos/Makefile.build
@@ -0,0 +1,109 @@
1# -*- makefile -*-
2
3SRCDIR = ../..
4
5all: kernel.bin loader.bin
6
7include ../../Make.config
8include ../Make.vars
9include ../../tests/Make.tests
10
11# Compiler and assembler options.
12kernel.bin: CPPFLAGS += -I$(SRCDIR)/lib/kernel
13
14# Core kernel.
15threads_SRC = threads/start.S # Startup code.
16threads_SRC += threads/init.c # Main program.
17threads_SRC += threads/thread.c # Thread management core.
18threads_SRC += threads/switch.S # Thread switch routine.
19threads_SRC += threads/interrupt.c # Interrupt core.
20threads_SRC += threads/intr-stubs.S # Interrupt stubs.
21threads_SRC += threads/synch.c # Synchronization.
22threads_SRC += threads/palloc.c # Page allocator.
23threads_SRC += threads/malloc.c # Subpage allocator.
24
25# Device driver code.
26devices_SRC = devices/pit.c # Programmable interrupt timer chip.
27devices_SRC += devices/timer.c # Periodic timer device.
28devices_SRC += devices/kbd.c # Keyboard device.
29devices_SRC += devices/vga.c # Video device.
30devices_SRC += devices/serial.c # Serial port device.
31devices_SRC += devices/block.c # Block device abstraction layer.
32devices_SRC += devices/partition.c # Partition block device.
33devices_SRC += devices/ide.c # IDE disk block device.
34devices_SRC += devices/input.c # Serial and keyboard input.
35devices_SRC += devices/intq.c # Interrupt queue.
36devices_SRC += devices/rtc.c # Real-time clock.
37devices_SRC += devices/shutdown.c # Reboot and power off.
38devices_SRC += devices/speaker.c # PC speaker.
39
40# Library code shared between kernel and user programs.
41lib_SRC = lib/debug.c # Debug helpers.
42lib_SRC += lib/random.c # Pseudo-random numbers.
43lib_SRC += lib/stdio.c # I/O library.
44lib_SRC += lib/stdlib.c # Utility functions.
45lib_SRC += lib/string.c # String functions.
46lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC.
47lib_SRC += lib/ustar.c # Unix standard tar format utilities.
48
49# Kernel-specific library code.
50lib/kernel_SRC = lib/kernel/debug.c # Debug helpers.
51lib/kernel_SRC += lib/kernel/list.c # Doubly-linked lists.
52lib/kernel_SRC += lib/kernel/bitmap.c # Bitmaps.
53lib/kernel_SRC += lib/kernel/hash.c # Hash tables.
54lib/kernel_SRC += lib/kernel/console.c # printf(), putchar().
55
56# User process code.
57userprog_SRC = userprog/process.c # Process loading.
58userprog_SRC += userprog/pagedir.c # Page directories.
59userprog_SRC += userprog/exception.c # User exception handler.
60userprog_SRC += userprog/syscall.c # System call handler.
61userprog_SRC += userprog/gdt.c # GDT initialization.
62userprog_SRC += userprog/tss.c # TSS management.
63
64# No virtual memory code yet.
65#vm_SRC = vm/file.c # Some file.
66
67# Filesystem code.
68filesys_SRC = filesys/filesys.c # Filesystem core.
69filesys_SRC += filesys/free-map.c # Free sector bitmap.
70filesys_SRC += filesys/file.c # Files.
71filesys_SRC += filesys/directory.c # Directories.
72filesys_SRC += filesys/inode.c # File headers.
73filesys_SRC += filesys/fsutil.c # Utilities.
74
75SOURCES = $(foreach dir,$(KERNEL_SUBDIRS),$($(dir)_SRC))
76OBJECTS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SOURCES)))
77DEPENDS = $(patsubst %.o,%.d,$(OBJECTS))
78
79threads/kernel.lds.s: CPPFLAGS += -P
80threads/kernel.lds.s: threads/kernel.lds.S threads/loader.h
81
82kernel.o: threads/kernel.lds.s $(OBJECTS)
83 $(LD) -T $< -o $@ $(OBJECTS)
84
85kernel.bin: kernel.o
86 $(OBJCOPY) -R .note -R .comment -S $< $@
87
88threads/loader.o: threads/loader.S
89 $(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES)
90
91loader.bin: threads/loader.o
92 $(LD) -N -e 0 -Ttext 0x7c00 --oformat binary -o $@ $<
93
94os.dsk: kernel.bin
95 cat $^ > $@
96
97clean::
98 rm -f $(OBJECTS) $(DEPENDS)
99 rm -f threads/loader.o threads/kernel.lds.s threads/loader.d
100 rm -f kernel.bin.tmp
101 rm -f kernel.o kernel.lds.s
102 rm -f kernel.bin loader.bin
103 rm -f bochsout.txt bochsrc.txt
104 rm -f results grade
105
106Makefile: $(SRCDIR)/Makefile.build
107 cp $< $@
108
109-include $(DEPENDS)
diff --git a/pintos-progos/Makefile.kernel b/pintos-progos/Makefile.kernel
new file mode 100644
index 0000000..162a411
--- /dev/null
+++ b/pintos-progos/Makefile.kernel
@@ -0,0 +1,20 @@
1# -*- makefile -*-
2
3all:
4
5include Make.vars
6
7DIRS = $(sort $(addprefix build/,$(KERNEL_SUBDIRS) $(TEST_SUBDIRS) lib/user))
8
9all grade check: $(DIRS) build/Makefile
10 cd build && $(MAKE) $@
11$(DIRS):
12 mkdir -p $@
13build/Makefile: ../Makefile.build
14 cp $< $@
15
16build/%: $(DIRS) build/Makefile
17 cd build && $(MAKE) $*
18
19clean:
20 rm -rf build
diff --git a/pintos-progos/Makefile.userprog b/pintos-progos/Makefile.userprog
new file mode 100644
index 0000000..0df391a
--- /dev/null
+++ b/pintos-progos/Makefile.userprog
@@ -0,0 +1,52 @@
1# -*- makefile -*-
2
3$(PROGS): CPPFLAGS += -I$(SRCDIR)/lib/user -I.
4
5# Linker flags.
6$(PROGS): LDFLAGS += -nostdlib -static -Wl,-T,$(LDSCRIPT)
7$(PROGS): LDSCRIPT = $(SRCDIR)/lib/user/user.lds
8
9# Library code shared between kernel and user programs.
10lib_SRC = lib/debug.c # Debug code.
11lib_SRC += lib/random.c # Pseudo-random numbers.
12lib_SRC += lib/stdio.c # I/O library.
13lib_SRC += lib/stdlib.c # Utility functions.
14lib_SRC += lib/string.c # String functions.
15lib_SRC += lib/arithmetic.c # 64-bit arithmetic for GCC.
16lib_SRC += lib/ustar.c # Unix standard tar format utilities.
17
18# User level only library code.
19lib/user_SRC = lib/user/debug.c # Debug helpers.
20lib/user_SRC += lib/user/syscall.c # System calls.
21lib/user_SRC += lib/user/console.c # Console code.
22
23LIB_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(lib_SRC) $(lib/user_SRC)))
24LIB_DEP = $(patsubst %.o,%.d,$(LIB_OBJ))
25LIB = lib/user/entry.o libc.a
26
27PROGS_SRC = $(foreach prog,$(PROGS),$($(prog)_SRC))
28PROGS_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(PROGS_SRC)))
29PROGS_DEP = $(patsubst %.o,%.d,$(PROGS_OBJ))
30
31all: $(PROGS)
32
33define TEMPLATE
34$(1)_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$($(1)_SRC)))
35$(1): $$($(1)_OBJ) $$(LIB) $$(LDSCRIPT)
36 $$(CC) $$(LDFLAGS) $$($(1)_OBJ) $$(LIB) -o $$@
37endef
38
39$(foreach prog,$(PROGS),$(eval $(call TEMPLATE,$(prog))))
40
41libc.a: $(LIB_OBJ)
42 rm -f $@
43 ar r $@ $^
44 ranlib $@
45
46clean::
47 rm -f $(PROGS) $(PROGS_OBJ) $(PROGS_DEP)
48 rm -f $(LIB_DEP) $(LIB_OBJ) lib/user/entry.[do] libc.a
49
50.PHONY: all clean
51
52-include $(LIB_DEP) $(PROGS_DEP)
diff --git a/pintos-progos/devices/block.c b/pintos-progos/devices/block.c
new file mode 100644
index 0000000..a3acec1
--- /dev/null
+++ b/pintos-progos/devices/block.c
@@ -0,0 +1,223 @@
1#include "devices/block.h"
2#include <list.h>
3#include <string.h>
4#include <stdio.h>
5#include "devices/ide.h"
6#include "threads/malloc.h"
7
8/* A block device. */
9struct block
10 {
11 struct list_elem list_elem; /* Element in all_blocks. */
12
13 char name[16]; /* Block device name. */
14 enum block_type type; /* Type of block device. */
15 block_sector_t size; /* Size in sectors. */
16
17 const struct block_operations *ops; /* Driver operations. */
18 void *aux; /* Extra data owned by driver. */
19
20 unsigned long long read_cnt; /* Number of sectors read. */
21 unsigned long long write_cnt; /* Number of sectors written. */
22 };
23
24/* List of all block devices. */
25static struct list all_blocks = LIST_INITIALIZER (all_blocks);
26
27/* The block block assigned to each Pintos role. */
28static struct block *block_by_role[BLOCK_ROLE_CNT];
29
30static struct block *list_elem_to_block (struct list_elem *);
31
32/* Returns a human-readable name for the given block device
33 TYPE. */
34const char *
35block_type_name (enum block_type type)
36{
37 static const char *block_type_names[BLOCK_CNT] =
38 {
39 "kernel",
40 "filesys",
41 "scratch",
42 "swap",
43 "raw",
44 "foreign",
45 };
46
47 ASSERT (type < BLOCK_CNT);
48 return block_type_names[type];
49}
50
51/* Returns the block device fulfilling the given ROLE, or a null
52 pointer if no block device has been assigned that role. */
53struct block *
54block_get_role (enum block_type role)
55{
56 ASSERT (role < BLOCK_ROLE_CNT);
57 return block_by_role[role];
58}
59
60/* Assigns BLOCK the given ROLE. */
61void
62block_set_role (enum block_type role, struct block *block)
63{
64 ASSERT (role < BLOCK_ROLE_CNT);
65 block_by_role[role] = block;
66}
67
68/* Returns the first block device in kernel probe order, or a
69 null pointer if no block devices are registered. */
70struct block *
71block_first (void)
72{
73 return list_elem_to_block (list_begin (&all_blocks));
74}
75
76/* Returns the block device following BLOCK in kernel probe
77 order, or a null pointer if BLOCK is the last block device. */
78struct block *
79block_next (struct block *block)
80{
81 return list_elem_to_block (list_next (&block->list_elem));
82}
83
84/* Returns the block device with the given NAME, or a null
85 pointer if no block device has that name. */
86struct block *
87block_get_by_name (const char *name)
88{
89 struct list_elem *e;
90
91 for (e = list_begin (&all_blocks); e != list_end (&all_blocks);
92 e = list_next (e))
93 {
94 struct block *block = list_entry (e, struct block, list_elem);
95 if (!strcmp (name, block->name))
96 return block;
97 }
98
99 return NULL;
100}
101
102/* Verifies that SECTOR is a valid offset within BLOCK.
103 Panics if not. */
104static void
105check_sector (struct block *block, block_sector_t sector)
106{
107 if (sector >= block->size)
108 {
109 /* We do not use ASSERT because we want to panic here
110 regardless of whether NDEBUG is defined. */
111 PANIC ("Access past end of device %s (sector=%"PRDSNu", "
112 "size=%"PRDSNu")\n", block_name (block), sector, block->size);
113 }
114}
115
116/* Reads sector SECTOR from BLOCK into BUFFER, which must
117 have room for BLOCK_SECTOR_SIZE bytes.
118 Internally synchronizes accesses to block devices, so external
119 per-block device locking is unneeded. */
120void
121block_read (struct block *block, block_sector_t sector, void *buffer)
122{
123 check_sector (block, sector);
124 block->ops->read (block->aux, sector, buffer);
125 block->read_cnt++;
126}
127
128/* Write sector SECTOR to BLOCK from BUFFER, which must contain
129 BLOCK_SECTOR_SIZE bytes. Returns after the block device has
130 acknowledged receiving the data.
131 Internally synchronizes accesses to block devices, so external
132 per-block device locking is unneeded. */
133void
134block_write (struct block *block, block_sector_t sector, const void *buffer)
135{
136 check_sector (block, sector);
137 ASSERT (block->type != BLOCK_FOREIGN);
138 block->ops->write (block->aux, sector, buffer);
139 block->write_cnt++;
140}
141
142/* Returns the number of sectors in BLOCK. */
143block_sector_t
144block_size (struct block *block)
145{
146 return block->size;
147}
148
149/* Returns BLOCK's name (e.g. "hda"). */
150const char *
151block_name (struct block *block)
152{
153 return block->name;
154}
155
156/* Returns BLOCK's type. */
157enum block_type
158block_type (struct block *block)
159{
160 return block->type;
161}
162
163/* Prints statistics for each block device used for a Pintos role. */
164void
165block_print_stats (void)
166{
167 int i;
168
169 for (i = 0; i < BLOCK_ROLE_CNT; i++)
170 {
171 struct block *block = block_by_role[i];
172 if (block != NULL)
173 {
174 printf ("%s (%s): %llu reads, %llu writes\n",
175 block->name, block_type_name (block->type),
176 block->read_cnt, block->write_cnt);
177 }
178 }
179}
180
181/* Registers a new block device with the given NAME. If
182 EXTRA_INFO is non-null, it is printed as part of a user
183 message. The block device's SIZE in sectors and its TYPE must
184 be provided, as well as the it operation functions OPS, which
185 will be passed AUX in each function call. */
186struct block *
187block_register (const char *name, enum block_type type,
188 const char *extra_info, block_sector_t size,
189 const struct block_operations *ops, void *aux)
190{
191 struct block *block = malloc (sizeof *block);
192 if (block == NULL)
193 PANIC ("Failed to allocate memory for block device descriptor");
194
195 list_push_back (&all_blocks, &block->list_elem);
196 strlcpy (block->name, name, sizeof block->name);
197 block->type = type;
198 block->size = size;
199 block->ops = ops;
200 block->aux = aux;
201 block->read_cnt = 0;
202 block->write_cnt = 0;
203
204 printf ("%s: %'"PRDSNu" sectors (", block->name, block->size);
205 print_human_readable_size ((uint64_t) block->size * BLOCK_SECTOR_SIZE);
206 printf (")");
207 if (extra_info != NULL)
208 printf (", %s", extra_info);
209 printf ("\n");
210
211 return block;
212}
213
214/* Returns the block device corresponding to LIST_ELEM, or a null
215 pointer if LIST_ELEM is the list end of all_blocks. */
216static struct block *
217list_elem_to_block (struct list_elem *list_elem)
218{
219 return (list_elem != list_end (&all_blocks)
220 ? list_entry (list_elem, struct block, list_elem)
221 : NULL);
222}
223
diff --git a/pintos-progos/devices/block.h b/pintos-progos/devices/block.h
new file mode 100644
index 0000000..21732d6
--- /dev/null
+++ b/pintos-progos/devices/block.h
@@ -0,0 +1,74 @@
1#ifndef DEVICES_BLOCK_H
2#define DEVICES_BLOCK_H
3
4#include <stddef.h>
5#include <inttypes.h>
6
7/* Size of a block device sector in bytes.
8 All IDE disks use this sector size, as do most USB and SCSI
9 disks. It's not worth it to try to cater to other sector
10 sizes in Pintos (yet). */
11#define BLOCK_SECTOR_SIZE 512
12
13/* Index of a block device sector.
14 Good enough for devices up to 2 TB. */
15typedef uint32_t block_sector_t;
16
17/* Format specifier for printf(), e.g.:
18 printf ("sector=%"PRDSNu"\n", sector); */
19#define PRDSNu PRIu32
20
21/* Higher-level interface for file systems, etc. */
22
23struct block;
24
25/* Type of a block device. */
26enum block_type
27 {
28 /* Block device types that play a role in Pintos. */
29 BLOCK_KERNEL, /* Pintos OS kernel. */
30 BLOCK_FILESYS, /* File system. */
31 BLOCK_SCRATCH, /* Scratch. */
32 BLOCK_SWAP, /* Swap. */
33 BLOCK_ROLE_CNT,
34
35 /* Other kinds of block devices that Pintos may see but does
36 not interact with. */
37 BLOCK_RAW = BLOCK_ROLE_CNT, /* "Raw" device with unidentified contents. */
38 BLOCK_FOREIGN, /* Owned by non-Pintos operating system. */
39 BLOCK_CNT /* Number of Pintos block types. */
40 };
41
42const char *block_type_name (enum block_type);
43
44/* Finding block devices. */
45struct block *block_get_role (enum block_type);
46void block_set_role (enum block_type, struct block *);
47struct block *block_get_by_name (const char *name);
48
49struct block *block_first (void);
50struct block *block_next (struct block *);
51
52/* Block device operations. */
53block_sector_t block_size (struct block *);
54void block_read (struct block *, block_sector_t, void *);
55void block_write (struct block *, block_sector_t, const void *);
56const char *block_name (struct block *);
57enum block_type block_type (struct block *);
58
59/* Statistics. */
60void block_print_stats (void);
61
62/* Lower-level interface to block device drivers. */
63
64struct block_operations
65 {
66 void (*read) (void *aux, block_sector_t, void *buffer);
67 void (*write) (void *aux, block_sector_t, const void *buffer);
68 };
69
70struct block *block_register (const char *name, enum block_type,
71 const char *extra_info, block_sector_t size,
72 const struct block_operations *, void *aux);
73
74#endif /* devices/block.h */
diff --git a/pintos-progos/devices/ide.c b/pintos-progos/devices/ide.c
new file mode 100644
index 0000000..2cc0292
--- /dev/null
+++ b/pintos-progos/devices/ide.c
@@ -0,0 +1,527 @@
1#include "devices/ide.h"
2#include <ctype.h>
3#include <debug.h>
4#include <stdbool.h>
5#include <stdio.h>
6#include "devices/block.h"
7#include "devices/partition.h"
8#include "devices/timer.h"
9#include "threads/io.h"
10#include "threads/interrupt.h"
11#include "threads/synch.h"
12
13/* The code in this file is an interface to an ATA (IDE)
14 controller. It attempts to comply to [ATA-3]. */
15
16/* ATA command block port addresses. */
17#define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0) /* Data. */
18#define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1) /* Error. */
19#define reg_nsect(CHANNEL) ((CHANNEL)->reg_base + 2) /* Sector Count. */
20#define reg_lbal(CHANNEL) ((CHANNEL)->reg_base + 3) /* LBA 0:7. */
21#define reg_lbam(CHANNEL) ((CHANNEL)->reg_base + 4) /* LBA 15:8. */
22#define reg_lbah(CHANNEL) ((CHANNEL)->reg_base + 5) /* LBA 23:16. */
23#define reg_device(CHANNEL) ((CHANNEL)->reg_base + 6) /* Device/LBA 27:24. */
24#define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7) /* Status (r/o). */
25#define reg_command(CHANNEL) reg_status (CHANNEL) /* Command (w/o). */
26
27/* ATA control block port addresses.
28 (If we supported non-legacy ATA controllers this would not be
29 flexible enough, but it's fine for what we do.) */
30#define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206) /* Control (w/o). */
31#define reg_alt_status(CHANNEL) reg_ctl (CHANNEL) /* Alt Status (r/o). */
32
33/* Alternate Status Register bits. */
34#define STA_BSY 0x80 /* Busy. */
35#define STA_DRDY 0x40 /* Device Ready. */
36#define STA_DRQ 0x08 /* Data Request. */
37
38/* Control Register bits. */
39#define CTL_SRST 0x04 /* Software Reset. */
40
41/* Device Register bits. */
42#define DEV_MBS 0xa0 /* Must be set. */
43#define DEV_LBA 0x40 /* Linear based addressing. */
44#define DEV_DEV 0x10 /* Select device: 0=master, 1=slave. */
45
46/* Commands.
47 Many more are defined but this is the small subset that we
48 use. */
49#define CMD_IDENTIFY_DEVICE 0xec /* IDENTIFY DEVICE. */
50#define CMD_READ_SECTOR_RETRY 0x20 /* READ SECTOR with retries. */
51#define CMD_WRITE_SECTOR_RETRY 0x30 /* WRITE SECTOR with retries. */
52
53/* An ATA device. */
54struct ata_disk
55 {
56 char name[8]; /* Name, e.g. "hda". */
57 struct channel *channel; /* Channel that disk is attached to. */
58 int dev_no; /* Device 0 or 1 for master or slave. */
59 bool is_ata; /* Is device an ATA disk? */
60 };
61
62/* An ATA channel (aka controller).
63 Each channel can control up to two disks. */
64struct channel
65 {
66 char name[8]; /* Name, e.g. "ide0". */
67 uint16_t reg_base; /* Base I/O port. */
68 uint8_t irq; /* Interrupt in use. */
69
70 struct lock lock; /* Must acquire to access the controller. */
71 bool expecting_interrupt; /* True if an interrupt is expected, false if
72 any interrupt would be spurious. */
73 struct semaphore completion_wait; /* Up'd by interrupt handler. */
74
75 struct ata_disk devices[2]; /* The devices on this channel. */
76 };
77
78/* We support the two "legacy" ATA channels found in a standard PC. */
79#define CHANNEL_CNT 2
80static struct channel channels[CHANNEL_CNT];
81
82static struct block_operations ide_operations;
83
84static void reset_channel (struct channel *);
85static bool check_device_type (struct ata_disk *);
86static void identify_ata_device (struct ata_disk *);
87
88static void select_sector (struct ata_disk *, block_sector_t);
89static void issue_pio_command (struct channel *, uint8_t command);
90static void input_sector (struct channel *, void *);
91static void output_sector (struct channel *, const void *);
92
93static void wait_until_idle (const struct ata_disk *);
94static bool wait_while_busy (const struct ata_disk *);
95static void select_device (const struct ata_disk *);
96static void select_device_wait (const struct ata_disk *);
97
98static void interrupt_handler (struct intr_frame *);
99
100/* Initialize the disk subsystem and detect disks. */
101void
102ide_init (void)
103{
104 size_t chan_no;
105
106 for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++)
107 {
108 struct channel *c = &channels[chan_no];
109 int dev_no;
110
111 /* Initialize channel. */
112 snprintf (c->name, sizeof c->name, "ide%zu", chan_no);
113 switch (chan_no)
114 {
115 case 0:
116 c->reg_base = 0x1f0;
117 c->irq = 14 + 0x20;
118 break;
119 case 1:
120 c->reg_base = 0x170;
121 c->irq = 15 + 0x20;
122 break;
123 default:
124 NOT_REACHED ();
125 }
126 lock_init (&c->lock);
127 c->expecting_interrupt = false;
128 sema_init (&c->completion_wait, 0);
129
130 /* Initialize devices. */
131 for (dev_no = 0; dev_no < 2; dev_no++)
132 {
133 struct ata_disk *d = &c->devices[dev_no];
134 snprintf (d->name, sizeof d->name,
135 "hd%c", 'a' + chan_no * 2 + dev_no);
136 d->channel = c;
137 d->dev_no = dev_no;
138 d->is_ata = false;
139 }
140
141 /* Register interrupt handler. */
142 intr_register_ext (c->irq, interrupt_handler, c->name);
143
144 /* Reset hardware. */
145 reset_channel (c);
146
147 /* Distinguish ATA hard disks from other devices. */
148 if (check_device_type (&c->devices[0]))
149 check_device_type (&c->devices[1]);
150
151 /* Read hard disk identity information. */
152 for (dev_no = 0; dev_no < 2; dev_no++)
153 if (c->devices[dev_no].is_ata)
154 identify_ata_device (&c->devices[dev_no]);
155 }
156}
157
158/* Disk detection and identification. */
159
160static char *descramble_ata_string (char *, int size);
161
162/* Resets an ATA channel and waits for any devices present on it
163 to finish the reset. */
164static void
165reset_channel (struct channel *c)
166{
167 bool present[2];
168 int dev_no;
169
170 /* The ATA reset sequence depends on which devices are present,
171 so we start by detecting device presence. */
172 for (dev_no = 0; dev_no < 2; dev_no++)
173 {
174 struct ata_disk *d = &c->devices[dev_no];
175
176 select_device (d);
177
178 outb (reg_nsect (c), 0x55);
179 outb (reg_lbal (c), 0xaa);
180
181 outb (reg_nsect (c), 0xaa);
182 outb (reg_lbal (c), 0x55);
183
184 outb (reg_nsect (c), 0x55);
185 outb (reg_lbal (c), 0xaa);
186
187 present[dev_no] = (inb (reg_nsect (c)) == 0x55
188 && inb (reg_lbal (c)) == 0xaa);
189 }
190
191 /* Issue soft reset sequence, which selects device 0 as a side effect.
192 Also enable interrupts. */
193 outb (reg_ctl (c), 0);
194 timer_usleep (10);
195 outb (reg_ctl (c), CTL_SRST);
196 timer_usleep (10);
197 outb (reg_ctl (c), 0);
198
199 timer_msleep (150);
200
201 /* Wait for device 0 to clear BSY. */
202 if (present[0])
203 {
204 select_device (&c->devices[0]);
205 wait_while_busy (&c->devices[0]);
206 }
207
208 /* Wait for device 1 to clear BSY. */
209 if (present[1])
210 {
211 int i;
212
213 select_device (&c->devices[1]);
214 for (i = 0; i < 3000; i++)
215 {
216 if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1)
217 break;
218 timer_msleep (10);
219 }
220 wait_while_busy (&c->devices[1]);
221 }
222}
223
224/* Checks whether device D is an ATA disk and sets D's is_ata
225 member appropriately. If D is device 0 (master), returns true
226 if it's possible that a slave (device 1) exists on this
227 channel. If D is device 1 (slave), the return value is not
228 meaningful. */
229static bool
230check_device_type (struct ata_disk *d)
231{
232 struct channel *c = d->channel;
233 uint8_t error, lbam, lbah, status;
234
235 select_device (d);
236
237 error = inb (reg_error (c));
238 lbam = inb (reg_lbam (c));
239 lbah = inb (reg_lbah (c));
240 status = inb (reg_status (c));
241
242 if ((error != 1 && (error != 0x81 || d->dev_no == 1))
243 || (status & STA_DRDY) == 0
244 || (status & STA_BSY) != 0)
245 {
246 d->is_ata = false;
247 return error != 0x81;
248 }
249 else
250 {
251 d->is_ata = (lbam == 0 && lbah == 0) || (lbam == 0x3c && lbah == 0xc3);
252 return true;
253 }
254}
255
256/* Sends an IDENTIFY DEVICE command to disk D and reads the
257 response. Registers the disk with the block device
258 layer. */
259static void
260identify_ata_device (struct ata_disk *d)
261{
262 struct channel *c = d->channel;
263 char id[BLOCK_SECTOR_SIZE];
264 block_sector_t capacity;
265 char *model, *serial;
266 char extra_info[128];
267 struct block *block;
268
269 ASSERT (d->is_ata);
270
271 /* Send the IDENTIFY DEVICE command, wait for an interrupt
272 indicating the device's response is ready, and read the data
273 into our buffer. */
274 select_device_wait (d);
275 issue_pio_command (c, CMD_IDENTIFY_DEVICE);
276 sema_down (&c->completion_wait);
277 if (!wait_while_busy (d))
278 {
279 d->is_ata = false;
280 return;
281 }
282 input_sector (c, id);
283
284 /* Calculate capacity.
285 Read model name and serial number. */
286 capacity = *(uint32_t *) &id[60 * 2];
287 model = descramble_ata_string (&id[10 * 2], 20);
288 serial = descramble_ata_string (&id[27 * 2], 40);
289 snprintf (extra_info, sizeof extra_info,
290 "model \"%s\", serial \"%s\"", model, serial);
291
292 /* Disable access to IDE disks over 1 GB, which are likely
293 physical IDE disks rather than virtual ones. If we don't
294 allow access to those, we're less likely to scribble on
295 someone's important data. You can disable this check by
296 hand if you really want to do so. */
297 if (capacity >= 1024 * 1024 * 1024 / BLOCK_SECTOR_SIZE)
298 {
299 printf ("%s: ignoring ", d->name);
300 print_human_readable_size (capacity * 512);
301 printf ("disk for safety\n");
302 d->is_ata = false;
303 return;
304 }
305
306 /* Register. */
307 block = block_register (d->name, BLOCK_RAW, extra_info, capacity,
308 &ide_operations, d);
309 partition_scan (block);
310}
311
312/* Translates STRING, which consists of SIZE bytes in a funky
313 format, into a null-terminated string in-place. Drops
314 trailing whitespace and null bytes. Returns STRING. */
315static char *
316descramble_ata_string (char *string, int size)
317{
318 int i;
319
320 /* Swap all pairs of bytes. */
321 for (i = 0; i + 1 < size; i += 2)
322 {
323 char tmp = string[i];
324 string[i] = string[i + 1];
325 string[i + 1] = tmp;
326 }
327
328 /* Find the last non-white, non-null character. */
329 for (size--; size > 0; size--)
330 {
331 int c = string[size - 1];
332 if (c != '\0' && !isspace (c))
333 break;
334 }
335 string[size] = '\0';
336
337 return string;
338}
339
340/* Reads sector SEC_NO from disk D into BUFFER, which must have
341 room for BLOCK_SECTOR_SIZE bytes.
342 Internally synchronizes accesses to disks, so external
343 per-disk locking is unneeded. */
344static void
345ide_read (void *d_, block_sector_t sec_no, void *buffer)
346{
347 struct ata_disk *d = d_;
348 struct channel *c = d->channel;
349 lock_acquire (&c->lock);
350 select_sector (d, sec_no);
351 issue_pio_command (c, CMD_READ_SECTOR_RETRY);
352 sema_down (&c->completion_wait);
353 if (!wait_while_busy (d))
354 PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no);
355 input_sector (c, buffer);
356 lock_release (&c->lock);
357}
358
359/* Write sector SEC_NO to disk D from BUFFER, which must contain
360 BLOCK_SECTOR_SIZE bytes. Returns after the disk has
361 acknowledged receiving the data.
362 Internally synchronizes accesses to disks, so external
363 per-disk locking is unneeded. */
364static void
365ide_write (void *d_, block_sector_t sec_no, const void *buffer)
366{
367 struct ata_disk *d = d_;
368 struct channel *c = d->channel;
369 lock_acquire (&c->lock);
370 select_sector (d, sec_no);
371 issue_pio_command (c, CMD_WRITE_SECTOR_RETRY);
372 if (!wait_while_busy (d))
373 PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no);
374 output_sector (c, buffer);
375 sema_down (&c->completion_wait);
376 lock_release (&c->lock);
377}
378
379static struct block_operations ide_operations =
380 {
381 ide_read,
382 ide_write
383 };
384
385/* Selects device D, waiting for it to become ready, and then
386 writes SEC_NO to the disk's sector selection registers. (We
387 use LBA mode.) */
388static void
389select_sector (struct ata_disk *d, block_sector_t sec_no)
390{
391 struct channel *c = d->channel;
392
393 ASSERT (sec_no < (1UL << 28));
394
395 select_device_wait (d);
396 outb (reg_nsect (c), 1);
397 outb (reg_lbal (c), sec_no);
398 outb (reg_lbam (c), sec_no >> 8);
399 outb (reg_lbah (c), (sec_no >> 16));
400 outb (reg_device (c),
401 DEV_MBS | DEV_LBA | (d->dev_no == 1 ? DEV_DEV : 0) | (sec_no >> 24));
402}
403
404/* Writes COMMAND to channel C and prepares for receiving a
405 completion interrupt. */
406static void
407issue_pio_command (struct channel *c, uint8_t command)
408{
409 /* Interrupts must be enabled or our semaphore will never be
410 up'd by the completion handler. */
411 ASSERT (intr_get_level () == INTR_ON);
412
413 c->expecting_interrupt = true;
414 outb (reg_command (c), command);
415}
416
417/* Reads a sector from channel C's data register in PIO mode into
418 SECTOR, which must have room for BLOCK_SECTOR_SIZE bytes. */
419static void
420input_sector (struct channel *c, void *sector)
421{
422 insw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2);
423}
424
425/* Writes SECTOR to channel C's data register in PIO mode.
426 SECTOR must contain BLOCK_SECTOR_SIZE bytes. */
427static void
428output_sector (struct channel *c, const void *sector)
429{
430 outsw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2);
431}
432
433/* Low-level ATA primitives. */
434
435/* Wait up to 10 seconds for the controller to become idle, that
436 is, for the BSY and DRQ bits to clear in the status register.
437
438 As a side effect, reading the status register clears any
439 pending interrupt. */
440static void
441wait_until_idle (const struct ata_disk *d)
442{
443 int i;
444
445 for (i = 0; i < 1000; i++)
446 {
447 if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0)
448 return;
449 timer_usleep (10);
450 }
451
452 printf ("%s: idle timeout\n", d->name);
453}
454
455/* Wait up to 30 seconds for disk D to clear BSY,
456 and then return the status of the DRQ bit.
457 The ATA standards say that a disk may take as long as that to
458 complete its reset. */
459static bool
460wait_while_busy (const struct ata_disk *d)
461{
462 struct channel *c = d->channel;
463 int i;
464
465 for (i = 0; i < 3000; i++)
466 {
467 if (i == 700)
468 printf ("%s: busy, waiting...", d->name);
469 if (!(inb (reg_alt_status (c)) & STA_BSY))
470 {
471 if (i >= 700)
472 printf ("ok\n");
473 return (inb (reg_alt_status (c)) & STA_DRQ) != 0;
474 }
475 timer_msleep (10);
476 }
477
478 printf ("failed\n");
479 return false;
480}
481
482/* Program D's channel so that D is now the selected disk. */
483static void
484select_device (const struct ata_disk *d)
485{
486 struct channel *c = d->channel;
487 uint8_t dev = DEV_MBS;
488 if (d->dev_no == 1)
489 dev |= DEV_DEV;
490 outb (reg_device (c), dev);
491 inb (reg_alt_status (c));
492 timer_nsleep (400);
493}
494
495/* Select disk D in its channel, as select_device(), but wait for
496 the channel to become idle before and after. */
497static void
498select_device_wait (const struct ata_disk *d)
499{
500 wait_until_idle (d);
501 select_device (d);
502 wait_until_idle (d);
503}
504
505/* ATA interrupt handler. */
506static void
507interrupt_handler (struct intr_frame *f)
508{
509 struct channel *c;
510
511 for (c = channels; c < channels + CHANNEL_CNT; c++)
512 if (f->vec_no == c->irq)
513 {
514 if (c->expecting_interrupt)
515 {
516 inb (reg_status (c)); /* Acknowledge interrupt. */
517 sema_up (&c->completion_wait); /* Wake up waiter. */
518 }
519 else
520 printf ("%s: unexpected interrupt\n", c->name);
521 return;
522 }
523
524 NOT_REACHED ();
525}
526
527
diff --git a/pintos-progos/devices/ide.h b/pintos-progos/devices/ide.h
new file mode 100644
index 0000000..b35da5e
--- /dev/null
+++ b/pintos-progos/devices/ide.h
@@ -0,0 +1,6 @@
1#ifndef DEVICES_IDE_H
2#define DEVICES_IDE_H
3
4void ide_init (void);
5
6#endif /* devices/ide.h */
diff --git a/pintos-progos/devices/input.c b/pintos-progos/devices/input.c
new file mode 100644
index 0000000..4a12160
--- /dev/null
+++ b/pintos-progos/devices/input.c
@@ -0,0 +1,52 @@
1#include "devices/input.h"
2#include <debug.h>
3#include "devices/intq.h"
4#include "devices/serial.h"
5
6/* Stores keys from the keyboard and serial port. */
7static struct intq buffer;
8
9/* Initializes the input buffer. */
10void
11input_init (void)
12{
13 intq_init (&buffer);
14}
15
16/* Adds a key to the input buffer.
17 Interrupts must be off and the buffer must not be full. */
18void
19input_putc (uint8_t key)
20{
21 ASSERT (intr_get_level () == INTR_OFF);
22 ASSERT (!intq_full (&buffer));
23
24 intq_putc (&buffer, key);
25 serial_notify ();
26}
27
28/* Retrieves a key from the input buffer.
29 If the buffer is empty, waits for a key to be pressed. */
30uint8_t
31input_getc (void)
32{
33 enum intr_level old_level;
34 uint8_t key;
35
36 old_level = intr_disable ();
37 key = intq_getc (&buffer);
38 serial_notify ();
39 intr_set_level (old_level);
40
41 return key;
42}
43
44/* Returns true if the input buffer is full,
45 false otherwise.
46 Interrupts must be off. */
47bool
48input_full (void)
49{
50 ASSERT (intr_get_level () == INTR_OFF);
51 return intq_full (&buffer);
52}
diff --git a/pintos-progos/devices/input.h b/pintos-progos/devices/input.h
new file mode 100644
index 0000000..a2f50e9
--- /dev/null
+++ b/pintos-progos/devices/input.h
@@ -0,0 +1,12 @@
1#ifndef DEVICES_INPUT_H
2#define DEVICES_INPUT_H
3
4#include <stdbool.h>
5#include <stdint.h>
6
7void input_init (void);
8void input_putc (uint8_t);
9uint8_t input_getc (void);
10bool input_full (void);
11
12#endif /* devices/input.h */
diff --git a/pintos-progos/devices/intq.c b/pintos-progos/devices/intq.c
new file mode 100644
index 0000000..40b23ae
--- /dev/null
+++ b/pintos-progos/devices/intq.c
@@ -0,0 +1,114 @@
1#include "devices/intq.h"
2#include <debug.h>
3#include "threads/thread.h"
4
5static int next (int pos);
6static void wait (struct intq *q, struct thread **waiter);
7static void signal (struct intq *q, struct thread **waiter);
8
9/* Initializes interrupt queue Q. */
10void
11intq_init (struct intq *q)
12{
13 lock_init (&q->lock);
14 q->not_full = q->not_empty = NULL;
15 q->head = q->tail = 0;
16}
17
18/* Returns true if Q is empty, false otherwise. */
19bool
20intq_empty (const struct intq *q)
21{
22 ASSERT (intr_get_level () == INTR_OFF);
23 return q->head == q->tail;
24}
25
26/* Returns true if Q is full, false otherwise. */
27bool
28intq_full (const struct intq *q)
29{
30 ASSERT (intr_get_level () == INTR_OFF);
31 return next (q->head) == q->tail;
32}
33
34/* Removes a byte from Q and returns it.
35 If Q is empty, sleeps until a byte is added.
36 When called from an interrupt handler, Q must not be empty. */
37uint8_t
38intq_getc (struct intq *q)
39{
40 uint8_t byte;
41
42 ASSERT (intr_get_level () == INTR_OFF);
43 while (intq_empty (q))
44 {
45 ASSERT (!intr_context ());
46 lock_acquire (&q->lock);
47 wait (q, &q->not_empty);
48 lock_release (&q->lock);
49 }
50
51 byte = q->buf[q->tail];
52 q->tail = next (q->tail);
53 signal (q, &q->not_full);
54 return byte;
55}
56
57/* Adds BYTE to the end of Q.
58 If Q is full, sleeps until a byte is removed.
59 When called from an interrupt handler, Q must not be full. */
60void
61intq_putc (struct intq *q, uint8_t byte)
62{
63 ASSERT (intr_get_level () == INTR_OFF);
64 while (intq_full (q))
65 {
66 ASSERT (!intr_context ());
67 lock_acquire (&q->lock);
68 wait (q, &q->not_full);
69 lock_release (&q->lock);
70 }
71
72 q->buf[q->head] = byte;
73 q->head = next (q->head);
74 signal (q, &q->not_empty);
75}
76
77/* Returns the position after POS within an intq. */
78static int
79next (int pos)
80{
81 return (pos + 1) % INTQ_BUFSIZE;
82}
83
84/* WAITER must be the address of Q's not_empty or not_full
85 member. Waits until the given condition is true. */
86static void
87wait (struct intq *q UNUSED, struct thread **waiter)
88{
89 ASSERT (!intr_context ());
90 ASSERT (intr_get_level () == INTR_OFF);
91 ASSERT ((waiter == &q->not_empty && intq_empty (q))
92 || (waiter == &q->not_full && intq_full (q)));
93
94 *waiter = thread_current ();
95 thread_block ();
96}
97
98/* WAITER must be the address of Q's not_empty or not_full
99 member, and the associated condition must be true. If a
100 thread is waiting for the condition, wakes it up and resets
101 the waiting thread. */
102static void
103signal (struct intq *q UNUSED, struct thread **waiter)
104{
105 ASSERT (intr_get_level () == INTR_OFF);
106 ASSERT ((waiter == &q->not_empty && !intq_empty (q))
107 || (waiter == &q->not_full && !intq_full (q)));
108
109 if (*waiter != NULL)
110 {
111 thread_unblock (*waiter);
112 *waiter = NULL;
113 }
114}
diff --git a/pintos-progos/devices/intq.h b/pintos-progos/devices/intq.h
new file mode 100644
index 0000000..2312b12
--- /dev/null
+++ b/pintos-progos/devices/intq.h
@@ -0,0 +1,43 @@
1#ifndef DEVICES_INTQ_H
2#define DEVICES_INTQ_H
3
4#include "threads/interrupt.h"
5#include "threads/synch.h"
6
7/* An "interrupt queue", a circular buffer shared between
8 kernel threads and external interrupt handlers.
9
10 Interrupt queue functions can be called from kernel threads or
11 from external interrupt handlers. Except for intq_init(),
12 interrupts must be off in either case.
13
14 The interrupt queue has the structure of a "monitor". Locks
15 and condition variables from threads/synch.h cannot be used in
16 this case, as they normally would, because they can only
17 protect kernel threads from one another, not from interrupt
18 handlers. */
19
20/* Queue buffer size, in bytes. */
21#define INTQ_BUFSIZE 64
22
23/* A circular queue of bytes. */
24struct intq
25 {
26 /* Waiting threads. */
27 struct lock lock; /* Only one thread may wait at once. */
28 struct thread *not_full; /* Thread waiting for not-full condition. */
29 struct thread *not_empty; /* Thread waiting for not-empty condition. */
30
31 /* Queue. */
32 uint8_t buf[INTQ_BUFSIZE]; /* Buffer. */
33 int head; /* New data is written here. */
34 int tail; /* Old data is read here. */
35 };
36
37void intq_init (struct intq *);
38bool intq_empty (const struct intq *);
39bool intq_full (const struct intq *);
40uint8_t intq_getc (struct intq *);
41void intq_putc (struct intq *, uint8_t);
42
43#endif /* devices/intq.h */
diff --git a/pintos-progos/devices/kbd.c b/pintos-progos/devices/kbd.c
new file mode 100644
index 0000000..fcc82be
--- /dev/null
+++ b/pintos-progos/devices/kbd.c
@@ -0,0 +1,213 @@
1#include "devices/kbd.h"
2#include <ctype.h>
3#include <debug.h>
4#include <stdio.h>
5#include <string.h>
6#include "devices/input.h"
7#include "devices/shutdown.h"
8#include "threads/interrupt.h"
9#include "threads/io.h"
10
11/* Keyboard data register port. */
12#define DATA_REG 0x60
13
14/* Current state of shift keys.
15 True if depressed, false otherwise. */
16static bool left_shift, right_shift; /* Left and right Shift keys. */
17static bool left_alt, right_alt; /* Left and right Alt keys. */
18static bool left_ctrl, right_ctrl; /* Left and right Ctl keys. */
19
20/* Status of Caps Lock.
21 True when on, false when off. */
22static bool caps_lock;
23
24/* Number of keys pressed. */
25static int64_t key_cnt;
26
27static intr_handler_func keyboard_interrupt;
28
29/* Initializes the keyboard. */
30void
31kbd_init (void)
32{
33 intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard");
34}
35
36/* Prints keyboard statistics. */
37void
38kbd_print_stats (void)
39{
40 printf ("Keyboard: %lld keys pressed\n", key_cnt);
41}
42
43/* Maps a set of contiguous scancodes into characters. */
44struct keymap
45 {
46 uint8_t first_scancode; /* First scancode. */
47 const char *chars; /* chars[0] has scancode first_scancode,
48 chars[1] has scancode first_scancode + 1,
49 and so on to the end of the string. */
50 };
51
52/* Keys that produce the same characters regardless of whether
53 the Shift keys are down. Case of letters is an exception
54 that we handle elsewhere. */
55static const struct keymap invariant_keymap[] =
56 {
57 {0x01, "\033"}, /* Escape. */
58 {0x0e, "\b"},
59 {0x0f, "\tQWERTYUIOP"},
60 {0x1c, "\r"},
61 {0x1e, "ASDFGHJKL"},
62 {0x2c, "ZXCVBNM"},
63 {0x37, "*"},
64 {0x39, " "},
65 {0x53, "\177"}, /* Delete. */
66 {0, NULL},
67 };
68
69/* Characters for keys pressed without Shift, for those keys
70 where it matters. */
71static const struct keymap unshifted_keymap[] =
72 {
73 {0x02, "1234567890-="},
74 {0x1a, "[]"},
75 {0x27, ";'`"},
76 {0x2b, "\\"},
77 {0x33, ",./"},
78 {0, NULL},
79 };
80
81/* Characters for keys pressed with Shift, for those keys where
82 it matters. */
83static const struct keymap shifted_keymap[] =
84 {
85 {0x02, "!@#$%^&*()_+"},
86 {0x1a, "{}"},
87 {0x27, ":\"~"},
88 {0x2b, "|"},
89 {0x33, "<>?"},
90 {0, NULL},
91 };
92
93static bool map_key (const struct keymap[], unsigned scancode, uint8_t *);
94
95static void
96keyboard_interrupt (struct intr_frame *args UNUSED)
97{
98 /* Status of shift keys. */
99 bool shift = left_shift || right_shift;
100 bool alt = left_alt || right_alt;
101 bool ctrl = left_ctrl || right_ctrl;
102
103 /* Keyboard scancode. */
104 unsigned code;
105
106 /* False if key pressed, true if key released. */
107 bool release;
108
109 /* Character that corresponds to `code'. */
110 uint8_t c;
111
112 /* Read scancode, including second byte if prefix code. */
113 code = inb (DATA_REG);
114 if (code == 0xe0)
115 code = (code << 8) | inb (DATA_REG);
116
117 /* Bit 0x80 distinguishes key press from key release
118 (even if there's a prefix). */
119 release = (code & 0x80) != 0;
120 code &= ~0x80u;
121
122 /* Interpret key. */
123 if (code == 0x3a)
124 {
125 /* Caps Lock. */
126 if (!release)
127 caps_lock = !caps_lock;
128 }
129 else if (map_key (invariant_keymap, code, &c)
130 || (!shift && map_key (unshifted_keymap, code, &c))
131 || (shift && map_key (shifted_keymap, code, &c)))
132 {
133 /* Ordinary character. */
134 if (!release)
135 {
136 /* Reboot if Ctrl+Alt+Del pressed. */
137 if (c == 0177 && ctrl && alt)
138 shutdown_reboot ();
139
140 /* Handle Ctrl, Shift.
141 Note that Ctrl overrides Shift. */
142 if (ctrl && c >= 0x40 && c < 0x60)
143 {
144 /* A is 0x41, Ctrl+A is 0x01, etc. */
145 c -= 0x40;
146 }
147 else if (shift == caps_lock)
148 c = tolower (c);
149
150 /* Handle Alt by setting the high bit.
151 This 0x80 is unrelated to the one used to
152 distinguish key press from key release. */
153 if (alt)
154 c += 0x80;
155
156 /* Append to keyboard buffer. */
157 if (!input_full ())
158 {
159 key_cnt++;
160 input_putc (c);
161 }
162 }
163 }
164 else
165 {
166 /* Maps a keycode into a shift state variable. */
167 struct shift_key
168 {
169 unsigned scancode;
170 bool *state_var;
171 };
172
173 /* Table of shift keys. */
174 static const struct shift_key shift_keys[] =
175 {
176 { 0x2a, &left_shift},
177 { 0x36, &right_shift},
178 { 0x38, &left_alt},
179 {0xe038, &right_alt},
180 { 0x1d, &left_ctrl},
181 {0xe01d, &right_ctrl},
182 {0, NULL},
183 };
184
185 const struct shift_key *key;
186
187 /* Scan the table. */
188 for (key = shift_keys; key->scancode != 0; key++)
189 if (key->scancode == code)
190 {
191 *key->state_var = !release;
192 break;
193 }
194 }
195}
196
197/* Scans the array of keymaps K for SCANCODE.
198 If found, sets *C to the corresponding character and returns
199 true.
200 If not found, returns false and C is ignored. */
201static bool
202map_key (const struct keymap k[], unsigned scancode, uint8_t *c)
203{
204 for (; k->first_scancode != 0; k++)
205 if (scancode >= k->first_scancode
206 && scancode < k->first_scancode + strlen (k->chars))
207 {
208 *c = k->chars[scancode - k->first_scancode];
209 return true;
210 }
211
212 return false;
213}
diff --git a/pintos-progos/devices/kbd.h b/pintos-progos/devices/kbd.h
new file mode 100644
index 0000000..ed9c06b
--- /dev/null
+++ b/pintos-progos/devices/kbd.h
@@ -0,0 +1,9 @@
1#ifndef DEVICES_KBD_H
2#define DEVICES_KBD_H
3
4#include <stdint.h>
5
6void kbd_init (void);
7void kbd_print_stats (void);
8
9#endif /* devices/kbd.h */
diff --git a/pintos-progos/devices/partition.c b/pintos-progos/devices/partition.c
new file mode 100644
index 0000000..7e97332
--- /dev/null
+++ b/pintos-progos/devices/partition.c
@@ -0,0 +1,324 @@
1#include "devices/partition.h"
2#include <packed.h>
3#include <stdlib.h>
4#include <string.h>
5#include <stdio.h>
6#include "devices/block.h"
7#include "threads/malloc.h"
8
9/* A partition of a block device. */
10struct partition
11 {
12 struct block *block; /* Underlying block device. */
13 block_sector_t start; /* First sector within device. */
14 };
15
16static struct block_operations partition_operations;
17
18static void read_partition_table (struct block *, block_sector_t sector,
19 block_sector_t primary_extended_sector,
20 int *part_nr);
21static void found_partition (struct block *, uint8_t type,
22 block_sector_t start, block_sector_t size,
23 int part_nr);
24static const char *partition_type_name (uint8_t);
25
26/* Scans BLOCK for partitions of interest to Pintos. */
27void
28partition_scan (struct block *block)
29{
30 int part_nr = 0;
31 read_partition_table (block, 0, 0, &part_nr);
32 if (part_nr == 0)
33 printf ("%s: Device contains no partitions\n", block_name (block));
34}
35
36/* Reads the partition table in the given SECTOR of BLOCK and
37 scans it for partitions of interest to Pintos.
38
39 If SECTOR is 0, so that this is the top-level partition table
40 on BLOCK, then PRIMARY_EXTENDED_SECTOR is not meaningful;
41 otherwise, it should designate the sector of the top-level
42 extended partition table that was traversed to arrive at
43 SECTOR, for use in finding logical partitions (see the large
44 comment below).
45
46 PART_NR points to the number of non-empty primary or logical
47 partitions already encountered on BLOCK. It is incremented as
48 partitions are found. */
49static void
50read_partition_table (struct block *block, block_sector_t sector,
51 block_sector_t primary_extended_sector,
52 int *part_nr)
53{
54 /* Format of a partition table entry. See [Partitions]. */
55 struct partition_table_entry
56 {
57 uint8_t bootable; /* 0x00=not bootable, 0x80=bootable. */
58 uint8_t start_chs[3]; /* Encoded starting cylinder, head, sector. */
59 uint8_t type; /* Partition type (see partition_type_name). */
60 uint8_t end_chs[3]; /* Encoded ending cylinder, head, sector. */
61 uint32_t offset; /* Start sector offset from partition table. */
62 uint32_t size; /* Number of sectors. */
63 }
64 PACKED;
65
66 /* Partition table sector. */
67 struct partition_table
68 {
69 uint8_t loader[446]; /* Loader, in top-level partition table. */
70 struct partition_table_entry partitions[4]; /* Table entries. */
71 uint16_t signature; /* Should be 0xaa55. */
72 }
73 PACKED;
74
75 struct partition_table *pt;
76 size_t i;
77
78 /* Check SECTOR validity. */
79 if (sector >= block_size (block))
80 {
81 printf ("%s: Partition table at sector %"PRDSNu" past end of device.\n",
82 block_name (block), sector);
83 return;
84 }
85
86 /* Read sector. */
87 ASSERT (sizeof *pt == BLOCK_SECTOR_SIZE);
88 pt = malloc (sizeof *pt);
89 if (pt == NULL)
90 PANIC ("Failed to allocate memory for partition table.");
91 block_read (block, 0, pt);
92
93 /* Check signature. */
94 if (pt->signature != 0xaa55)
95 {
96 if (primary_extended_sector == 0)
97 printf ("%s: Invalid partition table signature\n", block_name (block));
98 else
99 printf ("%s: Invalid extended partition table in sector %"PRDSNu"\n",
100 block_name (block), sector);
101 free (pt);
102 return;
103 }
104
105 /* Parse partitions. */
106 for (i = 0; i < sizeof pt->partitions / sizeof *pt->partitions; i++)
107 {
108 struct partition_table_entry *e = &pt->partitions[i];
109
110 if (e->size == 0 || e->type == 0)
111 {
112 /* Ignore empty partition. */
113 }
114 else if (e->type == 0x05 /* Extended partition. */
115 || e->type == 0x0f /* Windows 98 extended partition. */
116 || e->type == 0x85 /* Linux extended partition. */
117 || e->type == 0xc5) /* DR-DOS extended partition. */
118 {
119 printf ("%s: Extended partition in sector %"PRDSNu"\n",
120 block_name (block), sector);
121
122 /* The interpretation of the offset field for extended
123 partitions is bizarre. When the extended partition
124 table entry is in the master boot record, that is,
125 the device's primary partition table in sector 0, then
126 the offset is an absolute sector number. Otherwise,
127 no matter how deep the partition table we're reading
128 is nested, the offset is relative to the start of
129 the extended partition that the MBR points to. */
130 if (sector == 0)
131 read_partition_table (block, e->offset, e->offset, part_nr);
132 else
133 read_partition_table (block, e->offset + primary_extended_sector,
134 primary_extended_sector, part_nr);
135 }
136 else
137 {
138 ++*part_nr;
139
140 found_partition (block, e->type, e->offset + sector,
141 e->size, *part_nr);
142 }
143 }
144
145 free (pt);
146}
147
148/* We have found a primary or logical partition of the given TYPE
149 on BLOCK, starting at sector START and continuing for SIZE
150 sectors, which we are giving the partition number PART_NR.
151 Check whether this is a partition of interest to Pintos, and
152 if so then add it to the proper element of partitions[]. */
153static void
154found_partition (struct block *block, uint8_t part_type,
155 block_sector_t start, block_sector_t size,
156 int part_nr)
157{
158 if (start >= block_size (block))
159 printf ("%s%d: Partition starts past end of device (sector %"PRDSNu")\n",
160 block_name (block), part_nr, start);
161 else if (start + size < start || start + size > block_size (block))
162 printf ("%s%d: Partition end (%"PRDSNu") past end of device (%"PRDSNu")\n",
163 block_name (block), part_nr, start + size, block_size (block));
164 else
165 {
166 enum block_type type = (part_type == 0x20 ? BLOCK_KERNEL
167 : part_type == 0x21 ? BLOCK_FILESYS
168 : part_type == 0x22 ? BLOCK_SCRATCH
169 : part_type == 0x23 ? BLOCK_SWAP
170 : BLOCK_FOREIGN);
171 struct partition *p;
172 char extra_info[128];
173 char name[16];
174
175 p = malloc (sizeof *p);
176 if (p == NULL)
177 PANIC ("Failed to allocate memory for partition descriptor");
178 p->block = block;
179 p->start = start;
180
181 snprintf (name, sizeof name, "%s%d", block_name (block), part_nr);
182 snprintf (extra_info, sizeof extra_info, "%s (%02x)",
183 partition_type_name (part_type), part_type);
184 block_register (name, type, extra_info, size, &partition_operations, p);
185 }
186}
187
188/* Returns a human-readable name for the given partition TYPE. */
189static const char *
190partition_type_name (uint8_t type)
191{
192 /* Name of each known type of partition.
193 From util-linux-2.12r/fdisk/i386_sys_types.c.
194 This initializer makes use of a C99 feature that allows
195 array elements to be initialized by index. */
196 static const char *type_names[256] =
197 {
198 [0x00] = "Empty",
199 [0x01] = "FAT12",
200 [0x02] = "XENIX root",
201 [0x03] = "XENIX usr",
202 [0x04] = "FAT16 <32M",
203 [0x05] = "Extended",
204 [0x06] = "FAT16",
205 [0x07] = "HPFS/NTFS",
206 [0x08] = "AIX",
207 [0x09] = "AIX bootable",
208 [0x0a] = "OS/2 Boot Manager",
209 [0x0b] = "W95 FAT32",
210 [0x0c] = "W95 FAT32 (LBA)",
211 [0x0e] = "W95 FAT16 (LBA)",
212 [0x0f] = "W95 Ext'd (LBA)",
213 [0x10] = "OPUS",
214 [0x11] = "Hidden FAT12",
215 [0x12] = "Compaq diagnostics",
216 [0x14] = "Hidden FAT16 <32M",
217 [0x16] = "Hidden FAT16",
218 [0x17] = "Hidden HPFS/NTFS",
219 [0x18] = "AST SmartSleep",
220 [0x1b] = "Hidden W95 FAT32",
221 [0x1c] = "Hidden W95 FAT32 (LBA)",
222 [0x1e] = "Hidden W95 FAT16 (LBA)",
223 [0x20] = "Pintos OS kernel",
224 [0x21] = "Pintos file system",
225 [0x22] = "Pintos scratch",
226 [0x23] = "Pintos swap",
227 [0x24] = "NEC DOS",
228 [0x39] = "Plan 9",
229 [0x3c] = "PartitionMagic recovery",
230 [0x40] = "Venix 80286",
231 [0x41] = "PPC PReP Boot",
232 [0x42] = "SFS",
233 [0x4d] = "QNX4.x",
234 [0x4e] = "QNX4.x 2nd part",
235 [0x4f] = "QNX4.x 3rd part",
236 [0x50] = "OnTrack DM",
237 [0x51] = "OnTrack DM6 Aux1",
238 [0x52] = "CP/M",
239 [0x53] = "OnTrack DM6 Aux3",
240 [0x54] = "OnTrackDM6",
241 [0x55] = "EZ-Drive",
242 [0x56] = "Golden Bow",
243 [0x5c] = "Priam Edisk",
244 [0x61] = "SpeedStor",
245 [0x63] = "GNU HURD or SysV",
246 [0x64] = "Novell Netware 286",
247 [0x65] = "Novell Netware 386",
248 [0x70] = "DiskSecure Multi-Boot",
249 [0x75] = "PC/IX",
250 [0x80] = "Old Minix",
251 [0x81] = "Minix / old Linux",
252 [0x82] = "Linux swap / Solaris",
253 [0x83] = "Linux",
254 [0x84] = "OS/2 hidden C: drive",
255 [0x85] = "Linux extended",
256 [0x86] = "NTFS volume set",
257 [0x87] = "NTFS volume set",
258 [0x88] = "Linux plaintext",
259 [0x8e] = "Linux LVM",
260 [0x93] = "Amoeba",
261 [0x94] = "Amoeba BBT",
262 [0x9f] = "BSD/OS",
263 [0xa0] = "IBM Thinkpad hibernation",
264 [0xa5] = "FreeBSD",
265 [0xa6] = "OpenBSD",
266 [0xa7] = "NeXTSTEP",
267 [0xa8] = "Darwin UFS",
268 [0xa9] = "NetBSD",
269 [0xab] = "Darwin boot",
270 [0xb7] = "BSDI fs",
271 [0xb8] = "BSDI swap",
272 [0xbb] = "Boot Wizard hidden",
273 [0xbe] = "Solaris boot",
274 [0xbf] = "Solaris",
275 [0xc1] = "DRDOS/sec (FAT-12)",
276 [0xc4] = "DRDOS/sec (FAT-16 < 32M)",
277 [0xc6] = "DRDOS/sec (FAT-16)",
278 [0xc7] = "Syrinx",
279 [0xda] = "Non-FS data",
280 [0xdb] = "CP/M / CTOS / ...",
281 [0xde] = "Dell Utility",
282 [0xdf] = "BootIt",
283 [0xe1] = "DOS access",
284 [0xe3] = "DOS R/O",
285 [0xe4] = "SpeedStor",
286 [0xeb] = "BeOS fs",
287 [0xee] = "EFI GPT",
288 [0xef] = "EFI (FAT-12/16/32)",
289 [0xf0] = "Linux/PA-RISC boot",
290 [0xf1] = "SpeedStor",
291 [0xf4] = "SpeedStor",
292 [0xf2] = "DOS secondary",
293 [0xfd] = "Linux raid autodetect",
294 [0xfe] = "LANstep",
295 [0xff] = "BBT",
296 };
297
298 return type_names[type] != NULL ? type_names[type] : "Unknown";
299}
300
301/* Reads sector SECTOR from partition P into BUFFER, which must
302 have room for BLOCK_SECTOR_SIZE bytes. */
303static void
304partition_read (void *p_, block_sector_t sector, void *buffer)
305{
306 struct partition *p = p_;
307 block_read (p->block, p->start + sector, buffer);
308}
309
310/* Write sector SECTOR to partition P from BUFFER, which must
311 contain BLOCK_SECTOR_SIZE bytes. Returns after the block has
312 acknowledged receiving the data. */
313static void
314partition_write (void *p_, block_sector_t sector, const void *buffer)
315{
316 struct partition *p = p_;
317 block_write (p->block, p->start + sector, buffer);
318}
319
320static struct block_operations partition_operations =
321 {
322 partition_read,
323 partition_write
324 };
diff --git a/pintos-progos/devices/partition.h b/pintos-progos/devices/partition.h
new file mode 100644
index 0000000..47fea4d
--- /dev/null
+++ b/pintos-progos/devices/partition.h
@@ -0,0 +1,8 @@
1#ifndef DEVICES_PARTITION_H
2#define DEVICES_PARTITION_H
3
4struct block;
5
6void partition_scan (struct block *);
7
8#endif /* devices/partition.h */
diff --git a/pintos-progos/devices/pit.c b/pintos-progos/devices/pit.c
new file mode 100644
index 0000000..bfb1889
--- /dev/null
+++ b/pintos-progos/devices/pit.c
@@ -0,0 +1,83 @@
1#include "devices/pit.h"
2#include <debug.h>
3#include <stdint.h>
4#include "threads/interrupt.h"
5#include "threads/io.h"
6
7/* Interface to 8254 Programmable Interrupt Timer (PIT).
8 Refer to [8254] for details. */
9
10/* 8254 registers. */
11#define PIT_PORT_CONTROL 0x43 /* Control port. */
12#define PIT_PORT_COUNTER(CHANNEL) (0x40 + (CHANNEL)) /* Counter port. */
13
14/* PIT cycles per second. */
15#define PIT_HZ 1193180
16
17/* Configure the given CHANNEL in the PIT. In a PC, the PIT's
18 three output channels are hooked up like this:
19
20 - Channel 0 is connected to interrupt line 0, so that it can
21 be used as a periodic timer interrupt, as implemented in
22 Pintos in devices/timer.c.
23
24 - Channel 1 is used for dynamic RAM refresh (in older PCs).
25 No good can come of messing with this.
26
27 - Channel 2 is connected to the PC speaker, so that it can
28 be used to play a tone, as implemented in Pintos in
29 devices/speaker.c.
30
31 MODE specifies the form of output:
32
33 - Mode 2 is a periodic pulse: the channel's output is 1 for
34 most of the period, but drops to 0 briefly toward the end
35 of the period. This is useful for hooking up to an
36 interrupt controller to generate a periodic interrupt.
37
38 - Mode 3 is a square wave: for the first half of the period
39 it is 1, for the second half it is 0. This is useful for
40 generating a tone on a speaker.
41
42 - Other modes are less useful.
43
44 FREQUENCY is the number of periods per second, in Hz. */
45void
46pit_configure_channel (int channel, int mode, int frequency)
47{
48 uint16_t count;
49 enum intr_level old_level;
50
51 ASSERT (channel == 0 || channel == 2);
52 ASSERT (mode == 2 || mode == 3);
53
54 /* Convert FREQUENCY to a PIT counter value. The PIT has a
55 clock that runs at PIT_HZ cycles per second. We must
56 translate FREQUENCY into a number of these cycles. */
57 if (frequency < 19)
58 {
59 /* Frequency is too low: the quotient would overflow the
60 16-bit counter. Force it to 0, which the PIT treats as
61 65536, the highest possible count. This yields a 18.2
62 Hz timer, approximately. */
63 count = 0;
64 }
65 else if (frequency > PIT_HZ)
66 {
67 /* Frequency is too high: the quotient would underflow to
68 0, which the PIT would interpret as 65536. A count of 1
69 is illegal in mode 2, so we force it to 2, which yields
70 a 596.590 kHz timer, approximately. (This timer rate is
71 probably too fast to be useful anyhow.) */
72 count = 2;
73 }
74 else
75 count = (PIT_HZ + frequency / 2) / frequency;
76
77 /* Configure the PIT mode and load its counters. */
78 old_level = intr_disable ();
79 outb (PIT_PORT_CONTROL, (channel << 6) | 0x30 | (mode << 1));
80 outb (PIT_PORT_COUNTER (channel), count);
81 outb (PIT_PORT_COUNTER (channel), count >> 8);
82 intr_set_level (old_level);
83}
diff --git a/pintos-progos/devices/pit.h b/pintos-progos/devices/pit.h
new file mode 100644
index 0000000..dff36ae
--- /dev/null
+++ b/pintos-progos/devices/pit.h
@@ -0,0 +1,8 @@
1#ifndef DEVICES_PIT_H
2#define DEVICES_PIT_H
3
4#include <stdint.h>
5
6void pit_configure_channel (int channel, int mode, int frequency);
7
8#endif /* devices/pit.h */
diff --git a/pintos-progos/devices/rtc.c b/pintos-progos/devices/rtc.c
new file mode 100644
index 0000000..d99eb46
--- /dev/null
+++ b/pintos-progos/devices/rtc.c
@@ -0,0 +1,112 @@
1#include "devices/rtc.h"
2#include <stdio.h>
3#include "threads/io.h"
4
5/* This code is an interface to the MC146818A-compatible real
6 time clock found on PC motherboards. See [MC146818A] for
7 hardware details. */
8
9/* I/O register addresses. */
10#define CMOS_REG_SET 0x70 /* Selects CMOS register exposed by REG_IO. */
11#define CMOS_REG_IO 0x71 /* Contains the selected data byte. */
12
13/* Indexes of CMOS registers with real-time clock functions.
14 Note that all of these registers are in BCD format,
15 so that 0x59 means 59, not 89. */
16#define RTC_REG_SEC 0 /* Second: 0x00...0x59. */
17#define RTC_REG_MIN 2 /* Minute: 0x00...0x59. */
18#define RTC_REG_HOUR 4 /* Hour: 0x00...0x23. */
19#define RTC_REG_MDAY 7 /* Day of the month: 0x01...0x31. */
20#define RTC_REG_MON 8 /* Month: 0x01...0x12. */
21#define RTC_REG_YEAR 9 /* Year: 0x00...0x99. */
22
23/* Indexes of CMOS control registers. */
24#define RTC_REG_A 0x0a /* Register A: update-in-progress. */
25#define RTC_REG_B 0x0b /* Register B: 24/12 hour time, irq enables. */
26#define RTC_REG_C 0x0c /* Register C: pending interrupts. */
27#define RTC_REG_D 0x0d /* Register D: valid time? */
28
29/* Register A. */
30#define RTCSA_UIP 0x80 /* Set while time update in progress. */
31
32/* Register B. */
33#define RTCSB_SET 0x80 /* Disables update to let time be set. */
34#define RTCSB_DM 0x04 /* 0 = BCD time format, 1 = binary format. */
35#define RTCSB_24HR 0x02 /* 0 = 12-hour format, 1 = 24-hour format. */
36
37static int bcd_to_bin (uint8_t);
38static uint8_t cmos_read (uint8_t index);
39
40/* Returns number of seconds since Unix epoch of January 1,
41 1970. */
42time_t
43rtc_get_time (void)
44{
45 static const int days_per_month[12] =
46 {
47 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
48 };
49 int sec, min, hour, mday, mon, year;
50 time_t time;
51 int i;
52
53 /* Get time components.
54
55 We repeatedly read the time until it is stable from one read
56 to another, in case we start our initial read in the middle
57 of an update. This strategy is not recommended by the
58 MC146818A datasheet, but it is simpler than any of their
59 suggestions and, furthermore, it is also used by Linux.
60
61 The MC146818A can be configured for BCD or binary format,
62 but for historical reasons everyone always uses BCD format
63 except on obscure non-PC platforms, so we don't bother
64 trying to detect the format in use. */
65 do
66 {
67 sec = bcd_to_bin (cmos_read (RTC_REG_SEC));
68 min = bcd_to_bin (cmos_read (RTC_REG_MIN));
69 hour = bcd_to_bin (cmos_read (RTC_REG_HOUR));
70 mday = bcd_to_bin (cmos_read (RTC_REG_MDAY));
71 mon = bcd_to_bin (cmos_read (RTC_REG_MON));
72 year = bcd_to_bin (cmos_read (RTC_REG_YEAR));
73 }
74 while (sec != bcd_to_bin (cmos_read (RTC_REG_SEC)));
75
76 /* Translate years-since-1900 into years-since-1970.
77 If it's before the epoch, assume that it has passed 2000.
78 This will break at 2070, but that's long after our 31-bit
79 time_t breaks in 2038. */
80 if (year < 70)
81 year += 100;
82 year -= 70;
83
84 /* Break down all components into seconds. */
85 time = (year * 365 + (year - 1) / 4) * 24 * 60 * 60;
86 for (i = 1; i <= mon; i++)
87 time += days_per_month[i - 1] * 24 * 60 * 60;
88 if (mon > 2 && year % 4 == 0)
89 time += 24 * 60 * 60;
90 time += (mday - 1) * 24 * 60 * 60;
91 time += hour * 60 * 60;
92 time += min * 60;
93 time += sec;
94
95 return time;
96}
97
98/* Returns the integer value of the given BCD byte. */
99static int
100bcd_to_bin (uint8_t x)
101{
102 return (x & 0x0f) + ((x >> 4) * 10);
103}
104
105/* Reads a byte from the CMOS register with the given INDEX and
106 returns the byte read. */
107static uint8_t
108cmos_read (uint8_t index)
109{
110 outb (CMOS_REG_SET, index);
111 return inb (CMOS_REG_IO);
112}
diff --git a/pintos-progos/devices/rtc.h b/pintos-progos/devices/rtc.h
new file mode 100644
index 0000000..96a822f
--- /dev/null
+++ b/pintos-progos/devices/rtc.h
@@ -0,0 +1,8 @@
1#ifndef RTC_H
2#define RTC_H
3
4typedef unsigned long time_t;
5
6time_t rtc_get_time (void);
7
8#endif
diff --git a/pintos-progos/devices/serial.c b/pintos-progos/devices/serial.c
new file mode 100644
index 0000000..df770a7
--- /dev/null
+++ b/pintos-progos/devices/serial.c
@@ -0,0 +1,228 @@
1#include "devices/serial.h"
2#include <debug.h>
3#include "devices/input.h"
4#include "devices/intq.h"
5#include "devices/timer.h"
6#include "threads/io.h"
7#include "threads/interrupt.h"
8#include "threads/synch.h"
9#include "threads/thread.h"
10
11/* Register definitions for the 16550A UART used in PCs.
12 The 16550A has a lot more going on than shown here, but this
13 is all we need.
14
15 Refer to [PC16650D] for hardware information. */
16
17/* I/O port base address for the first serial port. */
18#define IO_BASE 0x3f8
19
20/* DLAB=0 registers. */
21#define RBR_REG (IO_BASE + 0) /* Receiver Buffer Reg. (read-only). */
22#define THR_REG (IO_BASE + 0) /* Transmitter Holding Reg. (write-only). */
23#define IER_REG (IO_BASE + 1) /* Interrupt Enable Reg.. */
24
25/* DLAB=1 registers. */
26#define LS_REG (IO_BASE + 0) /* Divisor Latch (LSB). */
27#define MS_REG (IO_BASE + 1) /* Divisor Latch (MSB). */
28
29/* DLAB-insensitive registers. */
30#define IIR_REG (IO_BASE + 2) /* Interrupt Identification Reg. (read-only) */
31#define FCR_REG (IO_BASE + 2) /* FIFO Control Reg. (write-only). */
32#define LCR_REG (IO_BASE + 3) /* Line Control Register. */
33#define MCR_REG (IO_BASE + 4) /* MODEM Control Register. */
34#define LSR_REG (IO_BASE + 5) /* Line Status Register (read-only). */
35
36/* Interrupt Enable Register bits. */
37#define IER_RECV 0x01 /* Interrupt when data received. */
38#define IER_XMIT 0x02 /* Interrupt when transmit finishes. */
39
40/* Line Control Register bits. */
41#define LCR_N81 0x03 /* No parity, 8 data bits, 1 stop bit. */
42#define LCR_DLAB 0x80 /* Divisor Latch Access Bit (DLAB). */
43
44/* MODEM Control Register. */
45#define MCR_OUT2 0x08 /* Output line 2. */
46
47/* Line Status Register. */
48#define LSR_DR 0x01 /* Data Ready: received data byte is in RBR. */
49#define LSR_THRE 0x20 /* THR Empty. */
50
51/* Transmission mode. */
52static enum { UNINIT, POLL, QUEUE } mode;
53
54/* Data to be transmitted. */
55static struct intq txq;
56
57static void set_serial (int bps);
58static void putc_poll (uint8_t);
59static void write_ier (void);
60static intr_handler_func serial_interrupt;
61
62/* Initializes the serial port device for polling mode.
63 Polling mode busy-waits for the serial port to become free
64 before writing to it. It's slow, but until interrupts have
65 been initialized it's all we can do. */
66static void
67init_poll (void)
68{
69 ASSERT (mode == UNINIT);
70 outb (IER_REG, 0); /* Turn off all interrupts. */
71 outb (FCR_REG, 0); /* Disable FIFO. */
72 set_serial (9600); /* 9.6 kbps, N-8-1. */
73 outb (MCR_REG, MCR_OUT2); /* Required to enable interrupts. */
74 intq_init (&txq);
75 mode = POLL;
76}
77
78/* Initializes the serial port device for queued interrupt-driven
79 I/O. With interrupt-driven I/O we don't waste CPU time
80 waiting for the serial device to become ready. */
81void
82serial_init_queue (void)
83{
84 enum intr_level old_level;
85
86 if (mode == UNINIT)
87 init_poll ();
88 ASSERT (mode == POLL);
89
90 intr_register_ext (0x20 + 4, serial_interrupt, "serial");
91 mode = QUEUE;
92 old_level = intr_disable ();
93 write_ier ();
94 intr_set_level (old_level);
95}
96
97/* Sends BYTE to the serial port. */
98void
99serial_putc (uint8_t byte)
100{
101 enum intr_level old_level = intr_disable ();
102
103 if (mode != QUEUE)
104 {
105 /* If we're not set up for interrupt-driven I/O yet,
106 use dumb polling to transmit a byte. */
107 if (mode == UNINIT)
108 init_poll ();
109 putc_poll (byte);
110 }
111 else
112 {
113 /* Otherwise, queue a byte and update the interrupt enable
114 register. */
115 if (old_level == INTR_OFF && intq_full (&txq))
116 {
117 /* Interrupts are off and the transmit queue is full.
118 If we wanted to wait for the queue to empty,
119 we'd have to reenable interrupts.
120 That's impolite, so we'll send a character via
121 polling instead. */
122 putc_poll (intq_getc (&txq));
123 }
124
125 intq_putc (&txq, byte);
126 write_ier ();
127 }
128
129 intr_set_level (old_level);
130}
131
132/* Flushes anything in the serial buffer out the port in polling
133 mode. */
134void
135serial_flush (void)
136{
137 enum intr_level old_level = intr_disable ();
138 while (!intq_empty (&txq))
139 putc_poll (intq_getc (&txq));
140 intr_set_level (old_level);
141}
142
143/* The fullness of the input buffer may have changed. Reassess
144 whether we should block receive interrupts.
145 Called by the input buffer routines when characters are added
146 to or removed from the buffer. */
147void
148serial_notify (void)
149{
150 ASSERT (intr_get_level () == INTR_OFF);
151 if (mode == QUEUE)
152 write_ier ();
153}
154
155/* Configures the serial port for BPS bits per second. */
156static void
157set_serial (int bps)
158{
159 int base_rate = 1843200 / 16; /* Base rate of 16550A, in Hz. */
160 uint16_t divisor = base_rate / bps; /* Clock rate divisor. */
161
162 ASSERT (bps >= 300 && bps <= 115200);
163
164 /* Enable DLAB. */
165 outb (LCR_REG, LCR_N81 | LCR_DLAB);
166
167 /* Set data rate. */
168 outb (LS_REG, divisor & 0xff);
169 outb (MS_REG, divisor >> 8);
170
171 /* Reset DLAB. */
172 outb (LCR_REG, LCR_N81);
173}
174
175/* Update interrupt enable register. */
176static void
177write_ier (void)
178{
179 uint8_t ier = 0;
180
181 ASSERT (intr_get_level () == INTR_OFF);
182
183 /* Enable transmit interrupt if we have any characters to
184 transmit. */
185 if (!intq_empty (&txq))
186 ier |= IER_XMIT;
187
188 /* Enable receive interrupt if we have room to store any
189 characters we receive. */
190 if (!input_full ())
191 ier |= IER_RECV;
192
193 outb (IER_REG, ier);
194}
195
196/* Polls the serial port until it's ready,
197 and then transmits BYTE. */
198static void
199putc_poll (uint8_t byte)
200{
201 ASSERT (intr_get_level () == INTR_OFF);
202
203 while ((inb (LSR_REG) & LSR_THRE) == 0)
204 continue;
205 outb (THR_REG, byte);
206}
207
208/* Serial interrupt handler. */
209static void
210serial_interrupt (struct intr_frame *f UNUSED)
211{
212 /* Inquire about interrupt in UART. Without this, we can
213 occasionally miss an interrupt running under QEMU. */
214 inb (IIR_REG);
215
216 /* As long as we have room to receive a byte, and the hardware
217 has a byte for us, receive a byte. */
218 while (!input_full () && (inb (LSR_REG) & LSR_DR) != 0)
219 input_putc (inb (RBR_REG));
220
221 /* As long as we have a byte to transmit, and the hardware is
222 ready to accept a byte for transmission, transmit a byte. */
223 while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0)
224 outb (THR_REG, intq_getc (&txq));
225
226 /* Update interrupt enable register based on queue status. */
227 write_ier ();
228}
diff --git a/pintos-progos/devices/serial.h b/pintos-progos/devices/serial.h
new file mode 100644
index 0000000..6e04778
--- /dev/null
+++ b/pintos-progos/devices/serial.h
@@ -0,0 +1,11 @@
1#ifndef DEVICES_SERIAL_H
2#define DEVICES_SERIAL_H
3
4#include <stdint.h>
5
6void serial_init_queue (void);
7void serial_putc (uint8_t);
8void serial_flush (void);
9void serial_notify (void);
10
11#endif /* devices/serial.h */
diff --git a/pintos-progos/devices/shutdown.c b/pintos-progos/devices/shutdown.c
new file mode 100644
index 0000000..7ff9a95
--- /dev/null
+++ b/pintos-progos/devices/shutdown.c
@@ -0,0 +1,131 @@
1#include "devices/shutdown.h"
2#include <console.h>
3#include <stdio.h>
4#include "devices/kbd.h"
5#include "devices/serial.h"
6#include "devices/timer.h"
7#include "threads/io.h"
8#include "threads/thread.h"
9#ifdef USERPROG
10#include "userprog/exception.h"
11#endif
12#ifdef FILESYS
13#include "devices/block.h"
14#include "filesys/filesys.h"
15#endif
16
17/* Keyboard control register port. */
18#define CONTROL_REG 0x64
19
20/* How to shut down when shutdown() is called. */
21static enum shutdown_type how = SHUTDOWN_NONE;
22
23static void print_stats (void);
24
25/* Shuts down the machine in the way configured by
26 shutdown_configure(). If the shutdown type is SHUTDOWN_NONE
27 (which is the default), returns without doing anything. */
28void
29shutdown (void)
30{
31 switch (how)
32 {
33 case SHUTDOWN_POWER_OFF:
34 shutdown_power_off ();
35 break;
36
37 case SHUTDOWN_REBOOT:
38 shutdown_reboot ();
39 break;
40
41 default:
42 /* Nothing to do. */
43 break;
44 }
45}
46
47/* Sets TYPE as the way that machine will shut down when Pintos
48 execution is complete. */
49void
50shutdown_configure (enum shutdown_type type)
51{
52 how = type;
53}
54
55/* Reboots the machine via the keyboard controller. */
56void
57shutdown_reboot (void)
58{
59 printf ("Rebooting...\n");
60
61 /* See [kbd] for details on how to program the keyboard
62 * controller. */
63 for (;;)
64 {
65 int i;
66
67 /* Poll keyboard controller's status byte until
68 * 'input buffer empty' is reported. */
69 for (i = 0; i < 0x10000; i++)
70 {
71 if ((inb (CONTROL_REG) & 0x02) == 0)
72 break;
73 timer_udelay (2);
74 }
75
76 timer_udelay (50);
77
78 /* Pulse bit 0 of the output port P2 of the keyboard controller.
79 * This will reset the CPU. */
80 outb (CONTROL_REG, 0xfe);
81 timer_udelay (50);
82 }
83}
84
85/* Powers down the machine we're running on,
86 as long as we're running on Bochs or QEMU. */
87void
88shutdown_power_off (void)
89{
90 const char s[] = "Shutdown";
91 const char *p;
92
93#ifdef FILESYS
94 filesys_done ();
95#endif
96
97 print_stats ();
98
99 printf ("Powering off...\n");
100 serial_flush ();
101
102 /* This is a special power-off sequence supported by Bochs and
103 QEMU, but not by physical hardware. */
104 for (p = s; *p != '\0'; p++)
105 outb (0x8900, *p);
106
107 /* This will power off a VMware VM if "gui.exitOnCLIHLT = TRUE"
108 is set in its configuration file. (The "pintos" script does
109 that automatically.) */
110 asm volatile ("cli; hlt" : : : "memory");
111
112 /* None of those worked. */
113 printf ("still running...\n");
114 for (;;);
115}
116
117/* Print statistics about Pintos execution. */
118static void
119print_stats (void)
120{
121 timer_print_stats ();
122 thread_print_stats ();
123#ifdef FILESYS
124 block_print_stats ();
125#endif
126 console_print_stats ();
127 kbd_print_stats ();
128#ifdef USERPROG
129 exception_print_stats ();
130#endif
131}
diff --git a/pintos-progos/devices/shutdown.h b/pintos-progos/devices/shutdown.h
new file mode 100644
index 0000000..dc4f942
--- /dev/null
+++ b/pintos-progos/devices/shutdown.h
@@ -0,0 +1,19 @@
1#ifndef DEVICES_SHUTDOWN_H
2#define DEVICES_SHUTDOWN_H
3
4#include <debug.h>
5
6/* How to shut down when Pintos has nothing left to do. */
7enum shutdown_type
8 {
9 SHUTDOWN_NONE, /* Loop forever. */
10 SHUTDOWN_POWER_OFF, /* Power off the machine (if possible). */
11 SHUTDOWN_REBOOT, /* Reboot the machine (if possible). */
12 };
13
14void shutdown (void);
15void shutdown_configure (enum shutdown_type);
16void shutdown_reboot (void) NO_RETURN;
17void shutdown_power_off (void) NO_RETURN;
18
19#endif /* devices/shutdown.h */
diff --git a/pintos-progos/devices/speaker.c b/pintos-progos/devices/speaker.c
new file mode 100644
index 0000000..5052005
--- /dev/null
+++ b/pintos-progos/devices/speaker.c
@@ -0,0 +1,68 @@
1#include "devices/speaker.h"
2#include "devices/pit.h"
3#include "threads/io.h"
4#include "threads/interrupt.h"
5#include "devices/timer.h"
6
7/* Speaker port enable I/O register. */
8#define SPEAKER_PORT_GATE 0x61
9
10/* Speaker port enable bits. */
11#define SPEAKER_GATE_ENABLE 0x03
12
13/* Sets the PC speaker to emit a tone at the given FREQUENCY, in
14 Hz. */
15void
16speaker_on (int frequency)
17{
18 if (frequency >= 20 && frequency <= 20000)
19 {
20 /* Set the timer channel that's connected to the speaker to
21 output a square wave at the given FREQUENCY, then
22 connect the timer channel output to the speaker. */
23 enum intr_level old_level = intr_disable ();
24 pit_configure_channel (2, 3, frequency);
25 outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) | SPEAKER_GATE_ENABLE);
26 intr_set_level (old_level);
27 }
28 else
29 {
30 /* FREQUENCY is outside the range of normal human hearing.
31 Just turn off the speaker. */
32 speaker_off ();
33 }
34}
35
36/* Turn off the PC speaker, by disconnecting the timer channel's
37 output from the speaker. */
38void
39speaker_off (void)
40{
41 enum intr_level old_level = intr_disable ();
42 outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) & ~SPEAKER_GATE_ENABLE);
43 intr_set_level (old_level);
44}
45
46/* Briefly beep the PC speaker. */
47void
48speaker_beep (void)
49{
50 /* Only attempt to beep the speaker if interrupts are enabled,
51 because we don't want to freeze the machine during the beep.
52 We could add a hook to the timer interrupt to avoid that
53 problem, but then we'd risk failing to ever stop the beep if
54 Pintos crashes for some unrelated reason. There's nothing
55 more annoying than a machine whose beeping you can't stop
56 without a power cycle.
57
58 We can't just enable interrupts while we sleep. For one
59 thing, we get called (indirectly) from printf, which should
60 always work, even during boot before we're ready to enable
61 interrupts. */
62 if (intr_get_level () == INTR_ON)
63 {
64 speaker_on (440);
65 timer_msleep (250);
66 speaker_off ();
67 }
68}
diff --git a/pintos-progos/devices/speaker.h b/pintos-progos/devices/speaker.h
new file mode 100644
index 0000000..98cef7b
--- /dev/null
+++ b/pintos-progos/devices/speaker.h
@@ -0,0 +1,8 @@
1#ifndef DEVICES_SPEAKER_H
2#define DEVICES_SPEAKER_H
3
4void speaker_on (int frequency);
5void speaker_off (void);
6void speaker_beep (void);
7
8#endif /* devices/speaker.h */
diff --git a/pintos-progos/devices/timer.c b/pintos-progos/devices/timer.c
new file mode 100644
index 0000000..befaaae
--- /dev/null
+++ b/pintos-progos/devices/timer.c
@@ -0,0 +1,246 @@
1#include "devices/timer.h"
2#include <debug.h>
3#include <inttypes.h>
4#include <round.h>
5#include <stdio.h>
6#include "devices/pit.h"
7#include "threads/interrupt.h"
8#include "threads/synch.h"
9#include "threads/thread.h"
10
11/* See [8254] for hardware details of the 8254 timer chip. */
12
13#if TIMER_FREQ < 19
14#error 8254 timer requires TIMER_FREQ >= 19
15#endif
16#if TIMER_FREQ > 1000
17#error TIMER_FREQ <= 1000 recommended
18#endif
19
20/* Number of timer ticks since OS booted. */
21static int64_t ticks;
22
23/* Number of loops per timer tick.
24 Initialized by timer_calibrate(). */
25static unsigned loops_per_tick;
26
27static intr_handler_func timer_interrupt;
28static bool too_many_loops (unsigned loops);
29static void busy_wait (int64_t loops);
30static void real_time_sleep (int64_t num, int32_t denom);
31static void real_time_delay (int64_t num, int32_t denom);
32
33/* Sets up the timer to interrupt TIMER_FREQ times per second,
34 and registers the corresponding interrupt. */
35void
36timer_init (void)
37{
38 pit_configure_channel (0, 2, TIMER_FREQ);
39 intr_register_ext (0x20, timer_interrupt, "8254 Timer");
40}
41
42/* Calibrates loops_per_tick, used to implement brief delays. */
43void
44timer_calibrate (void)
45{
46 unsigned high_bit, test_bit;
47
48 ASSERT (intr_get_level () == INTR_ON);
49 printf ("Calibrating timer... ");
50
51 /* Approximate loops_per_tick as the largest power-of-two
52 still less than one timer tick. */
53 loops_per_tick = 1u << 10;
54 while (!too_many_loops (loops_per_tick << 1))
55 {
56 loops_per_tick <<= 1;
57 ASSERT (loops_per_tick != 0);
58 }
59
60 /* Refine the next 8 bits of loops_per_tick. */
61 high_bit = loops_per_tick;
62 for (test_bit = high_bit >> 1; test_bit != high_bit >> 10; test_bit >>= 1)
63 if (!too_many_loops (high_bit | test_bit))
64 loops_per_tick |= test_bit;
65
66 printf ("%'"PRIu64" loops/s.\n", (uint64_t) loops_per_tick * TIMER_FREQ);
67}
68
69/* Returns the number of timer ticks since the OS booted. */
70int64_t
71timer_ticks (void)
72{
73 enum intr_level old_level = intr_disable ();
74 int64_t t = ticks;
75 intr_set_level (old_level);
76 return t;
77}
78
79/* Returns the number of timer ticks elapsed since THEN, which
80 should be a value once returned by timer_ticks(). */
81int64_t
82timer_elapsed (int64_t then)
83{
84 return timer_ticks () - then;
85}
86
87/* Sleeps for approximately TICKS timer ticks. Interrupts must
88 be turned on. */
89void
90timer_sleep (int64_t ticks)
91{
92 int64_t start = timer_ticks ();
93
94 ASSERT (intr_get_level () == INTR_ON);
95 while (timer_elapsed (start) < ticks)
96 thread_yield ();
97}
98
99/* Sleeps for approximately MS milliseconds. Interrupts must be
100 turned on. */
101void
102timer_msleep (int64_t ms)
103{
104 real_time_sleep (ms, 1000);
105}
106
107/* Sleeps for approximately US microseconds. Interrupts must be
108 turned on. */
109void
110timer_usleep (int64_t us)
111{
112 real_time_sleep (us, 1000 * 1000);
113}
114
115/* Sleeps for approximately NS nanoseconds. Interrupts must be
116 turned on. */
117void
118timer_nsleep (int64_t ns)
119{
120 real_time_sleep (ns, 1000 * 1000 * 1000);
121}
122
123/* Busy-waits for approximately MS milliseconds. Interrupts need
124 not be turned on.
125
126 Busy waiting wastes CPU cycles, and busy waiting with
127 interrupts off for the interval between timer ticks or longer
128 will cause timer ticks to be lost. Thus, use timer_msleep()
129 instead if interrupts are enabled. */
130void
131timer_mdelay (int64_t ms)
132{
133 real_time_delay (ms, 1000);
134}
135
136/* Sleeps for approximately US microseconds. Interrupts need not
137 be turned on.
138
139 Busy waiting wastes CPU cycles, and busy waiting with
140 interrupts off for the interval between timer ticks or longer
141 will cause timer ticks to be lost. Thus, use timer_usleep()
142 instead if interrupts are enabled. */
143void
144timer_udelay (int64_t us)
145{
146 real_time_delay (us, 1000 * 1000);
147}
148
149/* Sleeps execution for approximately NS nanoseconds. Interrupts
150 need not be turned on.
151
152 Busy waiting wastes CPU cycles, and busy waiting with
153 interrupts off for the interval between timer ticks or longer
154 will cause timer ticks to be lost. Thus, use timer_nsleep()
155 instead if interrupts are enabled.*/
156void
157timer_ndelay (int64_t ns)
158{
159 real_time_delay (ns, 1000 * 1000 * 1000);
160}
161
162/* Prints timer statistics. */
163void
164timer_print_stats (void)
165{
166 printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
167}
168
169/* Timer interrupt handler. */
170static void
171timer_interrupt (struct intr_frame *args UNUSED)
172{
173 ticks++;
174 thread_tick ();
175}
176
177/* Returns true if LOOPS iterations waits for more than one timer
178 tick, otherwise false. */
179static bool
180too_many_loops (unsigned loops)
181{
182 /* Wait for a timer tick. */
183 int64_t start = ticks;
184 while (ticks == start)
185 barrier ();
186
187 /* Run LOOPS loops. */
188 start = ticks;
189 busy_wait (loops);
190
191 /* If the tick count changed, we iterated too long. */
192 barrier ();
193 return start != ticks;
194}
195
196/* Iterates through a simple loop LOOPS times, for implementing
197 brief delays.
198
199 Marked NO_INLINE because code alignment can significantly
200 affect timings, so that if this function was inlined
201 differently in different places the results would be difficult
202 to predict. */
203static void NO_INLINE
204busy_wait (int64_t loops)
205{
206 while (loops-- > 0)
207 barrier ();
208}
209
210/* Sleep for approximately NUM/DENOM seconds. */
211static void
212real_time_sleep (int64_t num, int32_t denom)
213{
214 /* Convert NUM/DENOM seconds into timer ticks, rounding down.
215
216 (NUM / DENOM) s
217 ---------------------- = NUM * TIMER_FREQ / DENOM ticks.
218 1 s / TIMER_FREQ ticks
219 */
220 int64_t ticks = num * TIMER_FREQ / denom;
221
222 ASSERT (intr_get_level () == INTR_ON);
223 if (ticks > 0)
224 {
225 /* We're waiting for at least one full timer tick. Use
226 timer_sleep() because it will yield the CPU to other
227 processes. */
228 timer_sleep (ticks);
229 }
230 else
231 {
232 /* Otherwise, use a busy-wait loop for more accurate
233 sub-tick timing. */
234 real_time_delay (num, denom);
235 }
236}
237
238/* Busy-wait for approximately NUM/DENOM seconds. */
239static void
240real_time_delay (int64_t num, int32_t denom)
241{
242 /* Scale the numerator and denominator down by 1000 to avoid
243 the possibility of overflow. */
244 ASSERT (denom % 1000 == 0);
245 busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000));
246}
diff --git a/pintos-progos/devices/timer.h b/pintos-progos/devices/timer.h
new file mode 100644
index 0000000..cd3d6bb
--- /dev/null
+++ b/pintos-progos/devices/timer.h
@@ -0,0 +1,29 @@
1#ifndef DEVICES_TIMER_H
2#define DEVICES_TIMER_H
3
4#include <round.h>
5#include <stdint.h>
6
7/* Number of timer interrupts per second. */
8#define TIMER_FREQ 100
9
10void timer_init (void);
11void timer_calibrate (void);
12
13int64_t timer_ticks (void);
14int64_t timer_elapsed (int64_t);
15
16/* Sleep and yield the CPU to other threads. */
17void timer_sleep (int64_t ticks);
18void timer_msleep (int64_t milliseconds);
19void timer_usleep (int64_t microseconds);
20void timer_nsleep (int64_t nanoseconds);
21
22/* Busy waits. */
23void timer_mdelay (int64_t milliseconds);
24void timer_udelay (int64_t microseconds);
25void timer_ndelay (int64_t nanoseconds);
26
27void timer_print_stats (void);
28
29#endif /* devices/timer.h */
diff --git a/pintos-progos/devices/vga.c b/pintos-progos/devices/vga.c
new file mode 100644
index 0000000..f421b61
--- /dev/null
+++ b/pintos-progos/devices/vga.c
@@ -0,0 +1,172 @@
1#include "devices/vga.h"
2#include <round.h>
3#include <stdint.h>
4#include <stddef.h>
5#include <string.h>
6#include "devices/speaker.h"
7#include "threads/io.h"
8#include "threads/interrupt.h"
9#include "threads/vaddr.h"
10
11/* VGA text screen support. See [FREEVGA] for more information. */
12
13/* Number of columns and rows on the text display. */
14#define COL_CNT 80
15#define ROW_CNT 25
16
17/* Current cursor position. (0,0) is in the upper left corner of
18 the display. */
19static size_t cx, cy;
20
21/* Attribute value for gray text on a black background. */
22#define GRAY_ON_BLACK 0x07
23
24/* Framebuffer. See [FREEVGA] under "VGA Text Mode Operation".
25 The character at (x,y) is fb[y][x][0].
26 The attribute at (x,y) is fb[y][x][1]. */
27static uint8_t (*fb)[COL_CNT][2];
28
29static void clear_row (size_t y);
30static void cls (void);
31static void newline (void);
32static void move_cursor (void);
33static void find_cursor (size_t *x, size_t *y);
34
35/* Initializes the VGA text display. */
36static void
37init (void)
38{
39 /* Already initialized? */
40 static bool inited;
41 if (!inited)
42 {
43 fb = ptov (0xb8000);
44 find_cursor (&cx, &cy);
45 inited = true;
46 }
47}
48
49/* Writes C to the VGA text display, interpreting control
50 characters in the conventional ways. */
51void
52vga_putc (int c)
53{
54 /* Disable interrupts to lock out interrupt handlers
55 that might write to the console. */
56 enum intr_level old_level = intr_disable ();
57
58 init ();
59
60 switch (c)
61 {
62 case '\n':
63 newline ();
64 break;
65
66 case '\f':
67 cls ();
68 break;
69
70 case '\b':
71 if (cx > 0)
72 cx--;
73 break;
74
75 case '\r':
76 cx = 0;
77 break;
78
79 case '\t':
80 cx = ROUND_UP (cx + 1, 8);
81 if (cx >= COL_CNT)
82 newline ();
83 break;
84
85 case '\a':
86 intr_set_level (old_level);
87 speaker_beep ();
88 intr_disable ();
89 break;
90
91 default:
92 fb[cy][cx][0] = c;
93 fb[cy][cx][1] = GRAY_ON_BLACK;
94 if (++cx >= COL_CNT)
95 newline ();
96 break;
97 }
98
99 /* Update cursor position. */
100 move_cursor ();
101
102 intr_set_level (old_level);
103}
104
105/* Clears the screen and moves the cursor to the upper left. */
106static void
107cls (void)
108{
109 size_t y;
110
111 for (y = 0; y < ROW_CNT; y++)
112 clear_row (y);
113
114 cx = cy = 0;
115 move_cursor ();
116}
117
118/* Clears row Y to spaces. */
119static void
120clear_row (size_t y)
121{
122 size_t x;
123
124 for (x = 0; x < COL_CNT; x++)
125 {
126 fb[y][x][0] = ' ';
127 fb[y][x][1] = GRAY_ON_BLACK;
128 }
129}
130
131/* Advances the cursor to the first column in the next line on
132 the screen. If the cursor is already on the last line on the
133 screen, scrolls the screen upward one line. */
134static void
135newline (void)
136{
137 cx = 0;
138 cy++;
139 if (cy >= ROW_CNT)
140 {
141 cy = ROW_CNT - 1;
142 memmove (&fb[0], &fb[1], sizeof fb[0] * (ROW_CNT - 1));
143 clear_row (ROW_CNT - 1);
144 }
145}
146
147/* Moves the hardware cursor to (cx,cy). */
148static void
149move_cursor (void)
150{
151 /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
152 uint16_t cp = cx + COL_CNT * cy;
153 outw (0x3d4, 0x0e | (cp & 0xff00));
154 outw (0x3d4, 0x0f | (cp << 8));
155}
156
157/* Reads the current hardware cursor position into (*X,*Y). */
158static void
159find_cursor (size_t *x, size_t *y)
160{
161 /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
162 uint16_t cp;
163
164 outb (0x3d4, 0x0e);
165 cp = inb (0x3d5) << 8;
166
167 outb (0x3d4, 0x0f);
168 cp |= inb (0x3d5);
169
170 *x = cp % COL_CNT;
171 *y = cp / COL_CNT;
172}
diff --git a/pintos-progos/devices/vga.h b/pintos-progos/devices/vga.h
new file mode 100644
index 0000000..59690fb
--- /dev/null
+++ b/pintos-progos/devices/vga.h
@@ -0,0 +1,6 @@
1#ifndef DEVICES_VGA_H
2#define DEVICES_VGA_H
3
4void vga_putc (int);
5
6#endif /* devices/vga.h */
diff --git a/pintos-progos/examples/.gitignore b/pintos-progos/examples/.gitignore
new file mode 100644
index 0000000..a9e09d7
--- /dev/null
+++ b/pintos-progos/examples/.gitignore
@@ -0,0 +1,19 @@
1cat
2cmp
3cp
4echo
5halt
6hex-dump
7ls
8mcat
9mcp
10mkdir
11pwd
12rm
13shell
14bubsort
15insult
16lineup
17matmult
18recursor
19*.d
diff --git a/pintos-progos/examples/Makefile b/pintos-progos/examples/Makefile
new file mode 100644
index 0000000..f773950
--- /dev/null
+++ b/pintos-progos/examples/Makefile
@@ -0,0 +1,36 @@
1SRCDIR = ..
2
3# Test programs to compile, and a list of sources for each.
4# To add a new test, put its name on the PROGS list
5# and then add a name_SRC line that lists its source files.
6PROGS = cat cmp cp echo halt hello hex-dump ls mcat mcp mkdir pwd rm shell \
7 bubsort insult lineup matmult recursor test
8
9# Should work from project 2 onward.
10cat_SRC = cat.c
11cmp_SRC = cmp.c
12cp_SRC = cp.c
13echo_SRC = echo.c
14halt_SRC = halt.c
15hello_SRC = hello.c
16hex-dump_SRC = hex-dump.c
17insult_SRC = insult.c
18lineup_SRC = lineup.c
19ls_SRC = ls.c
20recursor_SRC = recursor.c
21rm_SRC = rm.c
22test_SRC = test.c
23
24# Should work in project 3; also in project 4 if VM is included.
25bubsort_SRC = bubsort.c
26matmult_SRC = matmult.c
27mcat_SRC = mcat.c
28mcp_SRC = mcp.c
29
30# Should work in project 4.
31mkdir_SRC = mkdir.c
32pwd_SRC = pwd.c
33shell_SRC = shell.c
34
35include $(SRCDIR)/Make.config
36include $(SRCDIR)/Makefile.userprog
diff --git a/pintos-progos/examples/bubsort.c b/pintos-progos/examples/bubsort.c
new file mode 100644
index 0000000..343219e
--- /dev/null
+++ b/pintos-progos/examples/bubsort.c
@@ -0,0 +1,38 @@
1/* sort.c
2
3 Test program to sort a large number of integers.
4
5 Intention is to stress virtual memory system.
6
7 Ideally, we could read the unsorted array off of the file
8 system, and store the result back to the file system! */
9#include <stdio.h>
10
11/* Size of array to sort. */
12#define SORT_SIZE 128
13
14int
15main (void)
16{
17 /* Array to sort. Static to reduce stack usage. */
18 static int array[SORT_SIZE];
19
20 int i, j, tmp;
21
22 /* First initialize the array in descending order. */
23 for (i = 0; i < SORT_SIZE; i++)
24 array[i] = SORT_SIZE - i - 1;
25
26 /* Then sort in ascending order. */
27 for (i = 0; i < SORT_SIZE - 1; i++)
28 for (j = 0; j < SORT_SIZE - 1 - i; j++)
29 if (array[j] > array[j + 1])
30 {
31 tmp = array[j];
32 array[j] = array[j + 1];
33 array[j + 1] = tmp;
34 }
35
36 printf ("sort exiting with code %d\n", array[0]);
37 return array[0];
38}
diff --git a/pintos-progos/examples/cat.c b/pintos-progos/examples/cat.c
new file mode 100644
index 0000000..c8d229d
--- /dev/null
+++ b/pintos-progos/examples/cat.c
@@ -0,0 +1,34 @@
1/* cat.c
2
3 Prints files specified on command line to the console. */
4
5#include <stdio.h>
6#include <syscall.h>
7
8int
9main (int argc, char *argv[])
10{
11 bool success = true;
12 int i;
13
14 for (i = 1; i < argc; i++)
15 {
16 int fd = open (argv[i]);
17 if (fd < 0)
18 {
19 printf ("%s: open failed\n", argv[i]);
20 success = false;
21 continue;
22 }
23 for (;;)
24 {
25 char buffer[1024];
26 int bytes_read = read (fd, buffer, sizeof buffer);
27 if (bytes_read == 0)
28 break;
29 write (STDOUT_FILENO, buffer, bytes_read);
30 }
31 close (fd);
32 }
33 return success ? EXIT_SUCCESS : EXIT_FAILURE;
34}
diff --git a/pintos-progos/examples/cmp.c b/pintos-progos/examples/cmp.c
new file mode 100644
index 0000000..94b406d
--- /dev/null
+++ b/pintos-progos/examples/cmp.c
@@ -0,0 +1,68 @@
1/* cat.c
2
3 Compares two files. */
4
5#include <stdio.h>
6#include <syscall.h>
7
8int
9main (int argc, char *argv[])
10{
11 int fd[2];
12
13 if (argc != 3)
14 {
15 printf ("usage: cmp A B\n");
16 return EXIT_FAILURE;
17 }
18
19 /* Open files. */
20 fd[0] = open (argv[1]);
21 if (fd[0] < 0)
22 {
23 printf ("%s: open failed\n", argv[1]);
24 return EXIT_FAILURE;
25 }
26 fd[1] = open (argv[2]);
27 if (fd[1] < 0)
28 {
29 printf ("%s: open failed\n", argv[1]);
30 return EXIT_FAILURE;
31 }
32
33 /* Compare data. */
34 for (;;)
35 {
36 int pos;
37 char buffer[2][1024];
38 int bytes_read[2];
39 int min_read;
40 int i;
41
42 pos = tell (fd[0]);
43 bytes_read[0] = read (fd[0], buffer[0], sizeof buffer[0]);
44 bytes_read[1] = read (fd[1], buffer[1], sizeof buffer[1]);
45 min_read = bytes_read[0] < bytes_read[1] ? bytes_read[0] : bytes_read[1];
46 if (min_read == 0)
47 break;
48
49 for (i = 0; i < min_read; i++)
50 if (buffer[0][i] != buffer[1][i])
51 {
52 printf ("Byte %d is %02hhx ('%c') in %s but %02hhx ('%c') in %s\n",
53 pos + i,
54 buffer[0][i], buffer[0][i], argv[1],
55 buffer[1][i], buffer[1][i], argv[2]);
56 return EXIT_FAILURE;
57 }
58
59 if (min_read < bytes_read[1])
60 printf ("%s is shorter than %s\n", argv[1], argv[2]);
61 else if (min_read < bytes_read[0])
62 printf ("%s is shorter than %s\n", argv[2], argv[1]);
63 }
64
65 printf ("%s and %s are identical\n", argv[1], argv[2]);
66
67 return EXIT_SUCCESS;
68}
diff --git a/pintos-progos/examples/cp.c b/pintos-progos/examples/cp.c
new file mode 100644
index 0000000..86a5cd7
--- /dev/null
+++ b/pintos-progos/examples/cp.c
@@ -0,0 +1,55 @@
1/* cat.c
2
3Copies one file to another. */
4
5#include <stdio.h>
6#include <syscall.h>
7
8int
9main (int argc, char *argv[])
10{
11 int in_fd, out_fd;
12
13 if (argc != 3)
14 {
15 printf ("usage: cp OLD NEW\n");
16 return EXIT_FAILURE;
17 }
18
19 /* Open input file. */
20 in_fd = open (argv[1]);
21 if (in_fd < 0)
22 {
23 printf ("%s: open failed\n", argv[1]);
24 return EXIT_FAILURE;
25 }
26
27 /* Create and open output file. */
28 if (!create (argv[2], filesize (in_fd)))
29 {
30 printf ("%s: create failed\n", argv[2]);
31 return EXIT_FAILURE;
32 }
33 out_fd = open (argv[2]);
34 if (out_fd < 0)
35 {
36 printf ("%s: open failed\n", argv[2]);
37 return EXIT_FAILURE;
38 }
39
40 /* Copy data. */
41 for (;;)
42 {
43 char buffer[1024];
44 int bytes_read = read (in_fd, buffer, sizeof buffer);
45 if (bytes_read == 0)
46 break;
47 if (write (out_fd, buffer, bytes_read) != bytes_read)
48 {
49 printf ("%s: write failed\n", argv[2]);
50 return EXIT_FAILURE;
51 }
52 }
53
54 return EXIT_SUCCESS;
55}
diff --git a/pintos-progos/examples/echo.c b/pintos-progos/examples/echo.c
new file mode 100644
index 0000000..1b136f2
--- /dev/null
+++ b/pintos-progos/examples/echo.c
@@ -0,0 +1,14 @@
1#include <stdio.h>
2#include <syscall.h>
3
4int
5main (int argc, char **argv)
6{
7 int i;
8
9 for (i = 0; i < argc; i++)
10 printf ("%s ", argv[i]);
11 printf ("\n");
12
13 return EXIT_SUCCESS;
14}
diff --git a/pintos-progos/examples/halt.c b/pintos-progos/examples/halt.c
new file mode 100644
index 0000000..bad7250
--- /dev/null
+++ b/pintos-progos/examples/halt.c
@@ -0,0 +1,14 @@
1/* halt.c
2
3 Simple program to test whether running a user program works.
4
5 Just invokes a system call that shuts down the OS. */
6
7#include <syscall.h>
8
9int
10main (void)
11{
12 halt ();
13 /* not reached */
14}
diff --git a/pintos-progos/examples/hello.c b/pintos-progos/examples/hello.c
new file mode 100644
index 0000000..a12fd94
--- /dev/null
+++ b/pintos-progos/examples/hello.c
@@ -0,0 +1,9 @@
1#include <stdio.h>
2#include <syscall.h>
3
4int
5main (int argc, char **argv)
6{
7 printf ("Hello World :)\n");
8 return EXIT_SUCCESS;
9}
diff --git a/pintos-progos/examples/hex-dump.c b/pintos-progos/examples/hex-dump.c
new file mode 100644
index 0000000..ee313f2
--- /dev/null
+++ b/pintos-progos/examples/hex-dump.c
@@ -0,0 +1,35 @@
1/* hex-dump.c
2
3 Prints files specified on command line to the console in hex. */
4
5#include <stdio.h>
6#include <syscall.h>
7
8int
9main (int argc, char *argv[])
10{
11 bool success = true;
12 int i;
13
14 for (i = 1; i < argc; i++)
15 {
16 int fd = open (argv[i]);
17 if (fd < 0)
18 {
19 printf ("%s: open failed\n", argv[i]);
20 success = false;
21 continue;
22 }
23 for (;;)
24 {
25 char buffer[1024];
26 int pos = tell (fd);
27 int bytes_read = read (fd, buffer, sizeof buffer);
28 if (bytes_read == 0)
29 break;
30 hex_dump (pos, buffer, bytes_read, true);
31 }
32 close (fd);
33 }
34 return success ? EXIT_SUCCESS : EXIT_FAILURE;
35}
diff --git a/pintos-progos/examples/insult.c b/pintos-progos/examples/insult.c
new file mode 100644
index 0000000..98c4e6a
--- /dev/null
+++ b/pintos-progos/examples/insult.c
@@ -0,0 +1,369 @@
1/* Insult.c
2
3 This is a version of the famous CS 107 random sentence
4 generator. I wrote a program that reads a grammar definition
5 file and writes a C file containing that grammar as hard code
6 static C strings. Thus the majority of the code below in
7 machine generated and totally unreadable. The arrays created
8 are specially designed to make generating the sentences as
9 easy as possible.
10
11 Originally by Greg Hutchins, March 1998.
12 Modified by Ben Pfaff for Pintos, Sept 2004. */
13char *start[] =
14 { "You", "1", "5", ".", "May", "13", ".", "With", "the", "19", "of", "18",
15",", "may", "13", "."
16};
17char startLoc[] = { 3, 0, 4, 7, 16 };
18char *adj[] = { "3", "4", "2", ",", "1" };
19char adjLoc[] = { 3, 0, 1, 2, 5 };
20char *adj3[] = { "3", "4" };
21char adj3Loc[] = { 2, 0, 1, 2 };
22char *adj1[] =
23 { "lame", "dried", "up", "par-broiled", "bloated", "half-baked", "spiteful",
24"egotistical", "ungrateful", "stupid", "moronic", "fat", "ugly", "puny", "pitiful",
25"insignificant", "blithering", "repulsive", "worthless", "blundering", "retarded",
26"useless", "obnoxious", "low-budget", "assinine", "neurotic", "subhuman", "crochety",
27"indescribable", "contemptible", "unspeakable", "sick", "lazy", "good-for-nothing",
28"slutty", "mentally-deficient", "creepy", "sloppy", "dismal", "pompous", "pathetic",
29"friendless", "revolting", "slovenly", "cantankerous", "uncultured", "insufferable",
30"gross", "unkempt", "defective", "crumby"
31};
32char adj1Loc[] =
33 { 50, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
3421, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
3543, 44, 45, 46, 47, 48, 49, 50, 51 };
36char *adj2[] =
37 { "putrefied", "festering", "funky", "moldy", "leprous", "curdled", "fetid",
38"slimy", "crusty", "sweaty", "damp", "deranged", "smelly", "stenchy", "malignant",
39"noxious", "grimy", "reeky", "nasty", "mutilated", "sloppy", "gruesome", "grisly",
40"sloshy", "wormy", "mealy", "spoiled", "contaminated", "rancid", "musty",
41"fly-covered", "moth-eaten", "decaying", "decomposed", "freeze-dried", "defective",
42"petrified", "rotting", "scabrous", "hirsute"
43};
44char adj2Loc[] =
45 { 40, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
4620, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 };
47char *name[] =
48 { "10", ",", "bad", "excuse", "for", "6", ",", "6", "for", "brains", ",",
49"4", "11", "8", "for", "brains", "offspring", "of", "a", "motherless", "10", "7", "6",
50"7", "4", "11", "8"
51};
52char nameLoc[] = { 7, 0, 1, 6, 10, 16, 21, 23, 27 };
53char *stuff[] =
54 { "shit", "toe", "jam", "filth", "puss", "earwax", "leaf", "clippings",
55"bat", "guano", "mucus", "fungus", "mung", "refuse", "earwax", "spittoon", "spittle",
56"phlegm"
57};
58char stuffLoc[] = { 14, 0, 1, 3, 4, 5, 6, 8, 10, 11, 12, 13, 14, 15, 17, 18 };
59char *noun_and_prep[] =
60 { "bit", "of", "piece", "of", "vat", "of", "lump", "of", "crock", "of",
61"ball", "of", "tub", "of", "load", "of", "bucket", "of", "mound", "of", "glob", "of", "bag",
62"of", "heap", "of", "mountain", "of", "load", "of", "barrel", "of", "sack", "of", "blob", "of",
63"pile", "of", "truckload", "of", "vat", "of"
64};
65char noun_and_prepLoc[] =
66 { 21, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36,
6738, 40, 42 };
68char *organics[] =
69 { "droppings", "mung", "zits", "puckies", "tumors", "cysts", "tumors",
70"livers", "froth", "parts", "scabs", "guts", "entrails", "blubber", "carcuses", "gizards",
71"9"
72};
73char organicsLoc[] =
74 { 17, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
75char *body_parts[] =
76 { "kidneys", "genitals", "buttocks", "earlobes", "innards", "feet"
77};
78char body_partsLoc[] = { 6, 0, 1, 2, 3, 4, 5, 6 };
79char *noun[] =
80 { "pop", "tart", "warthog", "twinkie", "barnacle", "fondue", "pot",
81"cretin", "fuckwad", "moron", "ass", "neanderthal", "nincompoop", "simpleton", "11"
82};
83char nounLoc[] = { 13, 0, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
84char *animal[] =
85 { "donkey", "llama", "dingo", "lizard", "gekko", "lemur", "moose", "camel",
86"goat", "eel"
87};
88char animalLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
89char *good_verb[] =
90 { "love", "cuddle", "fondle", "adore", "smooch", "hug", "caress", "worship",
91"look", "at", "touch"
92};
93char good_verbLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11 };
94char *curse[] =
95 { "14", "20", "23", "14", "17", "20", "23", "14", "find", "your", "9",
96"suddenly", "delectable", "14", "and", "14", "seek", "a", "battleground", "23"
97};
98char curseLoc[] = { 4, 0, 3, 7, 13, 20 };
99char *afflictors[] =
100 { "15", "21", "15", "21", "15", "21", "15", "21", "a", "22", "Rush",
101"Limbaugh", "the", "hosts", "of", "Hades"
102};
103char afflictorsLoc[] = { 6, 0, 2, 4, 6, 8, 12, 16 };
104char *quantity[] =
105 { "a", "4", "hoard", "of", "a", "4", "pack", "of", "a", "truckload", "of",
106"a", "swarm", "of", "many", "an", "army", "of", "a", "4", "heard", "of", "a", "4",
107"platoon", "of", "a", "4", "and", "4", "group", "of", "16"
108};
109char quantityLoc[] = { 10, 0, 4, 8, 11, 14, 15, 18, 22, 26, 32, 33 };
110char *numbers[] =
111 { "a", "thousand", "three", "million", "ninty-nine", "nine-hundred,",
112"ninty-nine", "forty-two", "a", "gazillion", "sixty-eight", "times", "thirty-three"
113};
114char numbersLoc[] = { 7, 0, 2, 4, 5, 7, 8, 10, 13 };
115char *adv[] =
116 { "viciously", "manicly", "merrily", "happily", ",", "with", "the", "19",
117"of", "18", ",", "gleefully", ",", "with", "much", "ritualistic", "celebration", ",",
118"franticly"
119};
120char advLoc[] = { 8, 0, 1, 2, 3, 4, 11, 12, 18, 19 };
121char *metaphor[] =
122 { "an", "irate", "manticore", "Thor's", "belch", "Alah's", "fist", "16",
123"titans", "a", "particularly", "vicious", "she-bear", "in", "the", "midst", "of", "her",
124"menstrual", "cycle", "a", "pissed-off", "Jabberwock"
125};
126char metaphorLoc[] = { 6, 0, 3, 5, 7, 9, 20, 23 };
127char *force[] = { "force", "fury", "power", "rage" };
128char forceLoc[] = { 4, 0, 1, 2, 3, 4 };
129char *bad_action[] =
130 { "spit", "shimmy", "slobber", "find", "refuge", "find", "shelter", "dance",
131"retch", "vomit", "defecate", "erect", "a", "strip", "mall", "build", "a", "26", "have", "a",
132"religious", "experience", "discharge", "bodily", "waste", "fart", "dance", "drool",
133"lambada", "spill", "16", "rusty", "tacks", "bite", "you", "sneeze", "sing", "16",
134"campfire", "songs", "smite", "you", "16", "times", "construct", "a", "new", "home", "throw",
135"a", "party", "procreate"
136};
137char bad_actionLoc[] =
138 { 25, 0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 15, 18, 22, 25, 26, 27, 28, 29, 33,
13935, 36, 40, 44, 48, 51, 52 };
140char *beasties[] =
141 { "yaks", "22", "maggots", "22", "cockroaches", "stinging", "scorpions",
142"fleas", "22", "weasels", "22", "gnats", "South", "American", "killer", "bees", "spiders",
143"4", "monkeys", "22", "wiener-dogs", "22", "rats", "22", "wolverines", "4", ",", "22",
144"pit-fiends"
145};
146char beastiesLoc[] =
147 { 14, 0, 1, 3, 5, 7, 8, 10, 12, 16, 17, 19, 21, 23, 25, 29 };
148char *condition[] =
149 { "frothing", "manic", "crazed", "plague-ridden", "disease-carrying",
150"biting", "rabid", "blood-thirsty", "ravaging", "slavering"
151};
152char conditionLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
153char *place[] =
154 { "in", "24", "25", "upon", "your", "mother's", "grave", "on", "24", "best",
155"rug", "in", "the", "26", "you", "call", "home", "upon", "your", "heinie"
156};
157char placeLoc[] = { 5, 0, 3, 7, 11, 17, 20 };
158char *relation[] =
159 { "your", "your", "your", "your", "father's", "your", "mother's", "your",
160"grandma's"
161};
162char relationLoc[] = { 6, 0, 1, 2, 3, 5, 7, 9 };
163char *in_something[] =
164 { "entrails", "anal", "cavity", "shoes", "house", "pantry", "general",
165"direction", "pants", "bed"
166};
167char in_somethingLoc[] = { 8, 0, 1, 3, 4, 5, 6, 8, 9, 10 };
168char *bad_place[] =
169 { "rat", "hole", "sewer", "toxic", "dump", "oil", "refinery", "landfill",
170"porto-pottie"
171};
172char bad_placeLoc[] = { 6, 0, 2, 3, 5, 7, 8, 9 };
173char **daGrammar[27];
174char *daGLoc[27];
175
176static void
177init_grammar (void)
178{
179 daGrammar[0] = start;
180 daGLoc[0] = startLoc;
181 daGrammar[1] = adj;
182 daGLoc[1] = adjLoc;
183 daGrammar[2] = adj3;
184 daGLoc[2] = adj3Loc;
185 daGrammar[3] = adj1;
186 daGLoc[3] = adj1Loc;
187 daGrammar[4] = adj2;
188 daGLoc[4] = adj2Loc;
189 daGrammar[5] = name;
190 daGLoc[5] = nameLoc;
191 daGrammar[6] = stuff;
192 daGLoc[6] = stuffLoc;
193 daGrammar[7] = noun_and_prep;
194 daGLoc[7] = noun_and_prepLoc;
195 daGrammar[8] = organics;
196 daGLoc[8] = organicsLoc;
197 daGrammar[9] = body_parts;
198 daGLoc[9] = body_partsLoc;
199 daGrammar[10] = noun;
200 daGLoc[10] = nounLoc;
201 daGrammar[11] = animal;
202 daGLoc[11] = animalLoc;
203 daGrammar[12] = good_verb;
204 daGLoc[12] = good_verbLoc;
205 daGrammar[13] = curse;
206 daGLoc[13] = curseLoc;
207 daGrammar[14] = afflictors;
208 daGLoc[14] = afflictorsLoc;
209 daGrammar[15] = quantity;
210 daGLoc[15] = quantityLoc;
211 daGrammar[16] = numbers;
212 daGLoc[16] = numbersLoc;
213 daGrammar[17] = adv;
214 daGLoc[17] = advLoc;
215 daGrammar[18] = metaphor;
216 daGLoc[18] = metaphorLoc;
217 daGrammar[19] = force;
218 daGLoc[19] = forceLoc;
219 daGrammar[20] = bad_action;
220 daGLoc[20] = bad_actionLoc;
221 daGrammar[21] = beasties;
222 daGLoc[21] = beastiesLoc;
223 daGrammar[22] = condition;
224 daGLoc[22] = conditionLoc;
225 daGrammar[23] = place;
226 daGLoc[23] = placeLoc;
227 daGrammar[24] = relation;
228 daGLoc[24] = relationLoc;
229 daGrammar[25] = in_something;
230 daGLoc[25] = in_somethingLoc;
231 daGrammar[26] = bad_place;
232 daGLoc[26] = bad_placeLoc;
233}
234
235#include <ctype.h>
236#include <debug.h>
237#include <random.h>
238#include <stdio.h>
239#include <stdlib.h>
240#include <string.h>
241#include <syscall.h>
242
243void expand (int num, char **grammar[], char *location[], int handle);
244
245static void
246usage (int ret_code, const char *message, ...) PRINTF_FORMAT (2, 3);
247
248static void
249usage (int ret_code, const char *message, ...)
250{
251 va_list args;
252
253 if (message != NULL)
254 {
255 va_start (args, message);
256 vprintf (message, args);
257 va_end (args);
258 }
259
260 printf ("\n"
261 "Usage: insult [OPTION]...\n"
262 "Prints random insults to screen.\n\n"
263 " -h: this help message\n"
264 " -s <integer>: set the random seed (default 4951)\n"
265 " -n <integer>: choose number of insults (default 4)\n"
266 " -f <file>: redirect output to <file>\n");
267
268 exit (ret_code);
269}
270
271int
272main (int argc, char *argv[])
273{
274 int sentence_cnt, new_seed, i, file_flag, sent_flag, seed_flag;
275 int handle;
276
277 new_seed = 4951;
278 sentence_cnt = 4;
279 file_flag = 0;
280 seed_flag = 0;
281 sent_flag = 0;
282 handle = STDOUT_FILENO;
283
284 for (i = 1; i < argc; i++)
285 {
286 if (strcmp (argv[1], "-h") == 0)
287 usage (0, NULL);
288 else if (strcmp (argv[i], "-s") == 0)
289 {
290 if (seed_flag++)
291 usage (-1, "Can't have more than one seed");
292 if (++i >= argc)
293 usage (-1, "Missing value for -s");
294 new_seed = atoi (argv[i]);
295 }
296 else if (strcmp (argv[i], "-n") == 0)
297 {
298 if (sent_flag++)
299 usage (-1, "Can't have more than one sentence option");
300 if (++i >= argc)
301 usage (-1, "Missing value for -n");
302 sentence_cnt = atoi (argv[i]);
303 if (sentence_cnt < 1)
304 usage (-1, "Must have at least one sentence");
305 }
306 else if (strcmp (argv[i], "-f") == 0)
307 {
308 if (file_flag++)
309 usage (-1, "Can't have more than one output file");
310 if (++i >= argc)
311 usage (-1, "Missing value for -f");
312
313 /* Because files have fixed length in the basic Pintos
314 file system, the 0 argument means that this option
315 will not be useful until project 4 is
316 implemented. */
317 create (argv[i], 0);
318 handle = open (argv[i]);
319 if (handle < 0)
320 {
321 printf ("%s: open failed\n", argv[i]);
322 return EXIT_FAILURE;
323 }
324 }
325 else
326 usage (-1, "Unrecognized flag");
327 }
328
329 init_grammar ();
330
331 random_init (new_seed);
332 hprintf (handle, "\n");
333
334 for (i = 0; i < sentence_cnt; i++)
335 {
336 hprintf (handle, "\n");
337 expand (0, daGrammar, daGLoc, handle);
338 hprintf (handle, "\n\n");
339 }
340
341 if (file_flag)
342 close (handle);
343
344 return EXIT_SUCCESS;
345}
346
347void
348expand (int num, char **grammar[], char *location[], int handle)
349{
350 char *word;
351 int i, which, listStart, listEnd;
352
353 which = random_ulong () % location[num][0] + 1;
354 listStart = location[num][which];
355 listEnd = location[num][which + 1];
356 for (i = listStart; i < listEnd; i++)
357 {
358 word = grammar[num][i];
359 if (!isdigit (*word))
360 {
361 if (!ispunct (*word))
362 hprintf (handle, " ");
363 hprintf (handle, "%s", word);
364 }
365 else
366 expand (atoi (word), grammar, location, handle);
367 }
368
369}
diff --git a/pintos-progos/examples/lib/.gitignore b/pintos-progos/examples/lib/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/pintos-progos/examples/lib/.gitignore
@@ -0,0 +1 @@
*.d
diff --git a/pintos-progos/examples/lib/user/.dummy b/pintos-progos/examples/lib/user/.dummy
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pintos-progos/examples/lib/user/.dummy
diff --git a/pintos-progos/examples/lib/user/.gitignore b/pintos-progos/examples/lib/user/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/pintos-progos/examples/lib/user/.gitignore
@@ -0,0 +1 @@
*.d
diff --git a/pintos-progos/examples/lineup.c b/pintos-progos/examples/lineup.c
new file mode 100644
index 0000000..60402d0
--- /dev/null
+++ b/pintos-progos/examples/lineup.c
@@ -0,0 +1,46 @@
1/* lineup.c
2
3 Converts a file to uppercase in-place.
4
5 Incidentally, another way to do this while avoiding the seeks
6 would be to open the input file, then remove() it and reopen
7 it under another handle. Because of Unix deletion semantics
8 this works fine. */
9
10#include <ctype.h>
11#include <stdio.h>
12#include <syscall.h>
13
14int
15main (int argc, char *argv[])
16{
17 char buf[1024];
18 int handle;
19
20 if (argc != 2)
21 exit (1);
22
23 handle = open (argv[1]);
24 if (handle < 0)
25 exit (2);
26
27 for (;;)
28 {
29 int n, i;
30
31 n = read (handle, buf, sizeof buf);
32 if (n <= 0)
33 break;
34
35 for (i = 0; i < n; i++)
36 buf[i] = toupper ((unsigned char) buf[i]);
37
38 seek (handle, tell (handle) - n);
39 if (write (handle, buf, n) != n)
40 printf ("write failed\n");
41 }
42
43 close (handle);
44
45 return EXIT_SUCCESS;
46}
diff --git a/pintos-progos/examples/ls.c b/pintos-progos/examples/ls.c
new file mode 100644
index 0000000..fbe27a1
--- /dev/null
+++ b/pintos-progos/examples/ls.c
@@ -0,0 +1,90 @@
1/* ls.c
2
3 Lists the contents of the directory or directories named on
4 the command line, or of the current directory if none are
5 named.
6
7 By default, only the name of each file is printed. If "-l" is
8 given as the first argument, the type, size, and inumber of
9 each file is also printed. This won't work until project 4. */
10
11#include <syscall.h>
12#include <stdio.h>
13#include <string.h>
14
15static bool
16list_dir (const char *dir, bool verbose)
17{
18 int dir_fd = open (dir);
19 if (dir_fd == -1)
20 {
21 printf ("%s: not found\n", dir);
22 return false;
23 }
24
25 if (isdir (dir_fd))
26 {
27 char name[READDIR_MAX_LEN];
28
29 printf ("%s", dir);
30 if (verbose)
31 printf (" (inumber %d)", inumber (dir_fd));
32 printf (":\n");
33
34 while (readdir (dir_fd, name))
35 {
36 printf ("%s", name);
37 if (verbose)
38 {
39 char full_name[128];
40 int entry_fd;
41
42 snprintf (full_name, sizeof full_name, "%s/%s", dir, name);
43 entry_fd = open (full_name);
44
45 printf (": ");
46 if (entry_fd != -1)
47 {
48 if (isdir (entry_fd))
49 printf ("directory");
50 else
51 printf ("%d-byte file", filesize (entry_fd));
52 printf (", inumber %d", inumber (entry_fd));
53 }
54 else
55 printf ("open failed");
56 close (entry_fd);
57 }
58 printf ("\n");
59 }
60 }
61 else
62 printf ("%s: not a directory\n", dir);
63 close (dir_fd);
64 return true;
65}
66
67int
68main (int argc, char *argv[])
69{
70 bool success = true;
71 bool verbose = false;
72
73 if (argc > 1 && !strcmp (argv[1], "-l"))
74 {
75 verbose = true;
76 argv++;
77 argc--;
78 }
79
80 if (argc <= 1)
81 success = list_dir (".", verbose);
82 else
83 {
84 int i;
85 for (i = 1; i < argc; i++)
86 if (!list_dir (argv[i], verbose))
87 success = false;
88 }
89 return success ? EXIT_SUCCESS : EXIT_FAILURE;
90}
diff --git a/pintos-progos/examples/matmult.c b/pintos-progos/examples/matmult.c
new file mode 100644
index 0000000..4f0615f
--- /dev/null
+++ b/pintos-progos/examples/matmult.c
@@ -0,0 +1,57 @@
1/* matmult.c
2
3 Test program to do matrix multiplication on large arrays.
4
5 Intended to stress virtual memory system.
6
7 Ideally, we could read the matrices off of the file system,
8 and store the result back to the file system!
9 */
10
11#include <stdio.h>
12#include <syscall.h>
13
14/* You should define DIM to be large enough that the arrays
15 don't fit in physical memory.
16
17 Dim Memory
18 ------ --------
19 16 3 kB
20 64 48 kB
21 128 192 kB
22 256 768 kB
23 512 3,072 kB
24 1,024 12,288 kB
25 2,048 49,152 kB
26 4,096 196,608 kB
27 8,192 786,432 kB
28 16,384 3,145,728 kB */
29#define DIM 128
30
31int A[DIM][DIM];
32int B[DIM][DIM];
33int C[DIM][DIM];
34
35int
36main (void)
37{
38 int i, j, k;
39
40 /* Initialize the matrices. */
41 for (i = 0; i < DIM; i++)
42 for (j = 0; j < DIM; j++)
43 {
44 A[i][j] = i;
45 B[i][j] = j;
46 C[i][j] = 0;
47 }
48
49 /* Multiply matrices. */
50 for (i = 0; i < DIM; i++)
51 for (j = 0; j < DIM; j++)
52 for (k = 0; k < DIM; k++)
53 C[i][j] += A[i][k] * B[k][j];
54
55 /* Done. */
56 exit (C[DIM - 1][DIM - 1]);
57}
diff --git a/pintos-progos/examples/mcat.c b/pintos-progos/examples/mcat.c
new file mode 100644
index 0000000..7b39760
--- /dev/null
+++ b/pintos-progos/examples/mcat.c
@@ -0,0 +1,45 @@
1/* mcat.c
2
3 Prints files specified on command line to the console, using
4 mmap. */
5
6#include <stdio.h>
7#include <syscall.h>
8
9int
10main (int argc, char *argv[])
11{
12 int i;
13
14 for (i = 1; i < argc; i++)
15 {
16 int fd;
17 mapid_t map;
18 void *data = (void *) 0x10000000;
19 int size;
20
21 /* Open input file. */
22 fd = open (argv[i]);
23 if (fd < 0)
24 {
25 printf ("%s: open failed\n", argv[i]);
26 return EXIT_FAILURE;
27 }
28 size = filesize (fd);
29
30 /* Map files. */
31 map = mmap (fd, data);
32 if (map == MAP_FAILED)
33 {
34 printf ("%s: mmap failed\n", argv[i]);
35 return EXIT_FAILURE;
36 }
37
38 /* Write file to console. */
39 write (STDOUT_FILENO, data, size);
40
41 /* Unmap files (optional). */
42 munmap (map);
43 }
44 return EXIT_SUCCESS;
45}
diff --git a/pintos-progos/examples/mcp.c b/pintos-progos/examples/mcp.c
new file mode 100644
index 0000000..6091dc8
--- /dev/null
+++ b/pintos-progos/examples/mcp.c
@@ -0,0 +1,68 @@
1/* mcp.c
2
3 Copies one file to another, using mmap. */
4
5#include <stdio.h>
6#include <string.h>
7#include <syscall.h>
8
9int
10main (int argc, char *argv[])
11{
12 int in_fd, out_fd;
13 mapid_t in_map, out_map;
14 void *in_data = (void *) 0x10000000;
15 void *out_data = (void *) 0x20000000;
16 int size;
17
18 if (argc != 3)
19 {
20 printf ("usage: cp OLD NEW\n");
21 return EXIT_FAILURE;
22 }
23
24 /* Open input file. */
25 in_fd = open (argv[1]);
26 if (in_fd < 0)
27 {
28 printf ("%s: open failed\n", argv[1]);
29 return EXIT_FAILURE;
30 }
31 size = filesize (in_fd);
32
33 /* Create and open output file. */
34 if (!create (argv[2], size))
35 {
36 printf ("%s: create failed\n", argv[2]);
37 return EXIT_FAILURE;
38 }
39 out_fd = open (argv[2]);
40 if (out_fd < 0)
41 {
42 printf ("%s: open failed\n", argv[2]);
43 return EXIT_FAILURE;
44 }
45
46 /* Map files. */
47 in_map = mmap (in_fd, in_data);
48 if (in_map == MAP_FAILED)
49 {
50 printf ("%s: mmap failed\n", argv[1]);
51 return EXIT_FAILURE;
52 }
53 out_map = mmap (out_fd, out_data);
54 if (out_map == MAP_FAILED)
55 {
56 printf ("%s: mmap failed\n", argv[2]);
57 return EXIT_FAILURE;
58 }
59
60 /* Copy files. */
61 memcpy (out_data, in_data, size);
62
63 /* Unmap files (optional). */
64 munmap (in_map);
65 munmap (out_map);
66
67 return EXIT_SUCCESS;
68}
diff --git a/pintos-progos/examples/mkdir.c b/pintos-progos/examples/mkdir.c
new file mode 100644
index 0000000..7ddbc3f
--- /dev/null
+++ b/pintos-progos/examples/mkdir.c
@@ -0,0 +1,24 @@
1/* mkdir.c
2
3 Creates a directory. */
4
5#include <stdio.h>
6#include <syscall.h>
7
8int
9main (int argc, char *argv[])
10{
11 if (argc != 2)
12 {
13 printf ("usage: %s DIRECTORY\n", argv[0]);
14 return EXIT_FAILURE;
15 }
16
17 if (!mkdir (argv[1]))
18 {
19 printf ("%s: mkdir failed\n", argv[1]);
20 return EXIT_FAILURE;
21 }
22
23 return EXIT_SUCCESS;
24}
diff --git a/pintos-progos/examples/pwd.c b/pintos-progos/examples/pwd.c
new file mode 100644
index 0000000..d2305cf
--- /dev/null
+++ b/pintos-progos/examples/pwd.c
@@ -0,0 +1,152 @@
1/* pwd.c
2
3 Prints the absolute name of the present working directory. */
4
5#include <syscall.h>
6#include <stdbool.h>
7#include <stdio.h>
8#include <string.h>
9
10static bool getcwd (char *cwd, size_t cwd_size);
11
12int
13main (void)
14{
15 char cwd[128];
16 if (getcwd (cwd, sizeof cwd))
17 {
18 printf ("%s\n", cwd);
19 return EXIT_SUCCESS;
20 }
21 else
22 {
23 printf ("error\n");
24 return EXIT_FAILURE;
25 }
26}
27
28/* Stores the inode number for FILE_NAME in *INUM.
29 Returns true if successful, false if the file could not be
30 opened. */
31static bool
32get_inumber (const char *file_name, int *inum)
33{
34 int fd = open (file_name);
35 if (fd >= 0)
36 {
37 *inum = inumber (fd);
38 close (fd);
39 return true;
40 }
41 else
42 return false;
43}
44
45/* Prepends PREFIX to the characters stored in the final *DST_LEN
46 bytes of the DST_SIZE-byte buffer that starts at DST.
47 Returns true if successful, false if adding that many
48 characters, plus a null terminator, would overflow the buffer.
49 (No null terminator is actually added or depended upon, but
50 its space is accounted for.) */
51static bool
52prepend (const char *prefix,
53 char *dst, size_t *dst_len, size_t dst_size)
54{
55 size_t prefix_len = strlen (prefix);
56 if (prefix_len + *dst_len + 1 <= dst_size)
57 {
58 *dst_len += prefix_len;
59 memcpy ((dst + dst_size) - *dst_len, prefix, prefix_len);
60 return true;
61 }
62 else
63 return false;
64}
65
66/* Stores the current working directory, as a null-terminated
67 string, in the CWD_SIZE bytes in CWD.
68 Returns true if successful, false on error. Errors include
69 system errors, directory trees deeper than MAX_LEVEL levels,
70 and insufficient space in CWD. */
71static bool
72getcwd (char *cwd, size_t cwd_size)
73{
74 size_t cwd_len = 0;
75
76#define MAX_LEVEL 20
77 char name[MAX_LEVEL * 3 + 1 + READDIR_MAX_LEN + 1];
78 char *namep;
79
80 int child_inum;
81
82 /* Make sure there's enough space for at least "/". */
83 if (cwd_size < 2)
84 return false;
85
86 /* Get inumber for current directory. */
87 if (!get_inumber (".", &child_inum))
88 return false;
89
90 namep = name;
91 for (;;)
92 {
93 int parent_inum, parent_fd;
94
95 /* Compose "../../../..", etc., in NAME. */
96 if ((namep - name) > MAX_LEVEL * 3)
97 return false;
98 *namep++ = '.';
99 *namep++ = '.';
100 *namep = '\0';
101
102 /* Open directory. */
103 parent_fd = open (name);
104 if (parent_fd < 0)
105 return false;
106 *namep++ = '/';
107
108 /* If parent and child have the same inumber,
109 then we've arrived at the root. */
110 parent_inum = inumber (parent_fd);
111 if (parent_inum == child_inum)
112 break;
113
114 /* Find name of file in parent directory with the child's
115 inumber. */
116 for (;;)
117 {
118 int test_inum;
119 if (!readdir (parent_fd, namep) || !get_inumber (name, &test_inum))
120 {
121 close (parent_fd);
122 return false;
123 }
124 if (test_inum == child_inum)
125 break;
126 }
127 close (parent_fd);
128
129 /* Prepend "/name" to CWD. */
130 if (!prepend (namep - 1, cwd, &cwd_len, cwd_size))
131 return false;
132
133 /* Move up. */
134 child_inum = parent_inum;
135 }
136
137 /* Finalize CWD. */
138 if (cwd_len > 0)
139 {
140 /* Move the string to the beginning of CWD,
141 and null-terminate it. */
142 memmove (cwd, (cwd + cwd_size) - cwd_len, cwd_len);
143 cwd[cwd_len] = '\0';
144 }
145 else
146 {
147 /* Special case for the root. */
148 strlcpy (cwd, "/", cwd_size);
149 }
150
151 return true;
152}
diff --git a/pintos-progos/examples/recursor.c b/pintos-progos/examples/recursor.c
new file mode 100644
index 0000000..79c784a
--- /dev/null
+++ b/pintos-progos/examples/recursor.c
@@ -0,0 +1,34 @@
1#include <stdio.h>
2#include <stdlib.h>
3#include <syscall.h>
4
5int
6main (int argc, char *argv[])
7{
8 char buffer[128];
9 pid_t pid;
10 int retval = 0;
11
12 if (argc != 4)
13 {
14 printf ("usage: recursor <string> <depth> <waitp>\n");
15 exit (1);
16 }
17
18 /* Print args. */
19 printf ("%s %s %s %s\n", argv[0], argv[1], argv[2], argv[3]);
20
21 /* Execute child and wait for it to finish if requested. */
22 if (atoi (argv[2]) != 0)
23 {
24 snprintf (buffer, sizeof buffer,
25 "recursor %s %d %s", argv[1], atoi (argv[2]) - 1, argv[3]);
26 pid = exec (buffer);
27 if (atoi (argv[3]))
28 retval = wait (pid);
29 }
30
31 /* Done. */
32 printf ("%s %s: dying, retval=%d\n", argv[1], argv[2], retval);
33 exit (retval);
34}
diff --git a/pintos-progos/examples/rm.c b/pintos-progos/examples/rm.c
new file mode 100644
index 0000000..0db7f7b
--- /dev/null
+++ b/pintos-progos/examples/rm.c
@@ -0,0 +1,21 @@
1/* rm.c
2
3 Removes files specified on command line. */
4
5#include <stdio.h>
6#include <syscall.h>
7
8int
9main (int argc, char *argv[])
10{
11 bool success = true;
12 int i;
13
14 for (i = 1; i < argc; i++)
15 if (!remove (argv[i]))
16 {
17 printf ("%s: remove failed\n", argv[i]);
18 success = false;
19 }
20 return success ? EXIT_SUCCESS : EXIT_FAILURE;
21}
diff --git a/pintos-progos/examples/shell.c b/pintos-progos/examples/shell.c
new file mode 100644
index 0000000..93641b4
--- /dev/null
+++ b/pintos-progos/examples/shell.c
@@ -0,0 +1,104 @@
1#include <stdbool.h>
2#include <stdio.h>
3#include <string.h>
4#include <syscall.h>
5
6static void read_line (char line[], size_t);
7static bool backspace (char **pos, char line[]);
8
9int
10main (void)
11{
12 printf ("Shell starting...\n");
13 for (;;)
14 {
15 char command[80];
16
17 /* Read command. */
18 printf ("--");
19 read_line (command, sizeof command);
20
21 /* Execute command. */
22 if (!strcmp (command, "exit"))
23 break;
24 else if (!memcmp (command, "cd ", 3))
25 {
26 if (!chdir (command + 3))
27 printf ("\"%s\": chdir failed\n", command + 3);
28 }
29 else if (command[0] == '\0')
30 {
31 /* Empty command. */
32 }
33 else
34 {
35 pid_t pid = exec (command);
36 if (pid != PID_ERROR)
37 printf ("\"%s\": exit code %d\n", command, wait (pid));
38 else
39 printf ("exec failed\n");
40 }
41 }
42
43 printf ("Shell exiting.");
44 return EXIT_SUCCESS;
45}
46
47/* Reads a line of input from the user into LINE, which has room
48 for SIZE bytes. Handles backspace and Ctrl+U in the ways
49 expected by Unix users. On return, LINE will always be
50 null-terminated and will not end in a new-line character. */
51static void
52read_line (char line[], size_t size)
53{
54 char *pos = line;
55 for (;;)
56 {
57 char c;
58 read (STDIN_FILENO, &c, 1);
59
60 switch (c)
61 {
62 case '\r':
63 *pos = '\0';
64 putchar ('\n');
65 return;
66
67 case '\b':
68 backspace (&pos, line);
69 break;
70
71 case ('U' - 'A') + 1: /* Ctrl+U. */
72 while (backspace (&pos, line))
73 continue;
74 break;
75
76 default:
77 /* Add character to line. */
78 if (pos < line + size - 1)
79 {
80 putchar (c);
81 *pos++ = c;
82 }
83 break;
84 }
85 }
86}
87
88/* If *POS is past the beginning of LINE, backs up one character
89 position. Returns true if successful, false if nothing was
90 done. */
91static bool
92backspace (char **pos, char line[])
93{
94 if (*pos > line)
95 {
96 /* Back up cursor, overwrite character, back up
97 again. */
98 printf ("\b \b");
99 (*pos)--;
100 return true;
101 }
102 else
103 return false;
104}
diff --git a/pintos-progos/examples/test.c b/pintos-progos/examples/test.c
new file mode 100644
index 0000000..44dc307
--- /dev/null
+++ b/pintos-progos/examples/test.c
@@ -0,0 +1,101 @@
1/* test.c
2
3 Experiments with syscalls
4 argc < 2 Print Hello World
5 argv[1][0] == 'p' print argv[2]
6 == 'e' Exec Test
7 == 'f' File test
8 == 'F' File descriptor stress test
9 == 'h' Halt
10 == '0' Null-Pointer Access
11*/
12
13#include <stdio.h>
14#include <syscall.h>
15
16#define LARGE_BUF_SIZE 4150
17char large_buf[LARGE_BUF_SIZE];
18
19#define NUM_EXEC_CHILDS 7
20char *execs[NUM_EXEC_CHILDS] = { "test", "test p FOO", "test p BAR", "test f", "test 0", &large_buf[0], "test^" };
21
22#define MAX_FD 4097
23
24static void init_args(void);
25static void init_args()
26{
27 int i = 0;
28 char *t = "";
29 while(i < LARGE_BUF_SIZE-1) {
30 if(!*t) t = "test ";
31 large_buf[i++] = *t++;
32 }
33 large_buf[LARGE_BUF_SIZE-1]='\0';
34}
35
36int
37main (int argc, char** argv)
38{
39 if(argc < 2) {
40 printf("Hello World!\n");
41 exit(0);
42 }
43 init_args();
44 if(argv[1][0] == 'e') {
45 int r = 0;
46 int i;
47 int tid[NUM_EXEC_CHILDS];
48
49 for(i = 0; i < NUM_EXEC_CHILDS; i++) {
50 tid[i] = exec(execs[i]);
51 }
52 for(i = 0; i < NUM_EXEC_CHILDS; i++) {
53 if (tid[i] >= 0) {
54 r = wait(tid[i]);
55 printf("P child %d exited with exit code %d\n",i, r);
56 } else {
57 printf("P child %d failed to start\n", i);
58 }
59 }
60 } else if(argv[1][0] == 'f') {
61 char buf[10];
62 int r;
63 create ("test.txt", 10);
64 int handle = open ("test.txt");
65 if (handle < 2)
66 printf ("open(test.txt) returned %d", handle);
67 if ((r=write(handle,"987654321",10)) != 10) {
68 printf("write failed: %d not %d\n",r,10);
69 exit(1);
70 }
71 seek(handle,0);
72 if ((r=read(handle, buf, 10)) != 10) {
73 printf("read failed: %d not %d\n",r,10);
74 exit(1);
75 }
76 printf("test.txt: %s\n", buf);
77 } else if(argv[1][0] == 'F') {
78 int j,i;
79 create ("foo.txt", 10);
80 for (j = 0; j < 5; j++) {
81 for (i = 2; i <= MAX_FD; i++) {
82 if (open ("foo.txt") < 0) {
83 printf("Opening the %d's file failed\n",i-2);
84 break;
85 }
86 }
87 while(--i >= 2) {
88 close (i);
89 }
90 }
91 } else if(argv[1][0] == '0') {
92 printf("Null pointer value is: %d\n",*((int*)NULL));
93 } else if(argv[1][0] == 'h') {
94 halt();
95 } else if(argv[1][0] == 'p' && argc >= 3) {
96 printf("%s\n", argv[2]);
97 } else {
98 printf("ARGV[1] is %s\n", argv[1]);
99 }
100 return 0;
101}
diff --git a/pintos-progos/filesys/.gitignore b/pintos-progos/filesys/.gitignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/pintos-progos/filesys/.gitignore
@@ -0,0 +1,3 @@
1build
2bochsrc.txt
3bochsout.txt
diff --git a/pintos-progos/filesys/Make.vars b/pintos-progos/filesys/Make.vars
new file mode 100644
index 0000000..b3aa005
--- /dev/null
+++ b/pintos-progos/filesys/Make.vars
@@ -0,0 +1,13 @@
1# -*- makefile -*-
2
3kernel.bin: DEFINES = -DUSERPROG -DFILESYS
4KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys
5TEST_SUBDIRS = tests/userprog tests/filesys/base tests/filesys/extended
6GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.no-vm
7SIMULATOR = --qemu
8
9# Uncomment the lines below to enable VM.
10#kernel.bin: DEFINES += -DVM
11#KERNEL_SUBDIRS += vm
12#TEST_SUBDIRS += tests/vm
13#GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.with-vm
diff --git a/pintos-progos/filesys/Makefile b/pintos-progos/filesys/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/pintos-progos/filesys/Makefile
@@ -0,0 +1 @@
include ../Makefile.kernel
diff --git a/pintos-progos/filesys/directory.c b/pintos-progos/filesys/directory.c
new file mode 100644
index 0000000..030c1c9
--- /dev/null
+++ b/pintos-progos/filesys/directory.c
@@ -0,0 +1,236 @@
1#include "filesys/directory.h"
2#include <stdio.h>
3#include <string.h>
4#include <list.h>
5#include "filesys/filesys.h"
6#include "filesys/inode.h"
7#include "threads/malloc.h"
8
9/* A directory. */
10struct dir
11 {
12 struct inode *inode; /* Backing store. */
13 off_t pos; /* Current position. */
14 };
15
16/* A single directory entry. */
17struct dir_entry
18 {
19 block_sector_t inode_sector; /* Sector number of header. */
20 char name[NAME_MAX + 1]; /* Null terminated file name. */
21 bool in_use; /* In use or free? */
22 };
23
24/* Creates a directory with space for ENTRY_CNT entries in the
25 given SECTOR. Returns true if successful, false on failure. */
26bool
27dir_create (block_sector_t sector, size_t entry_cnt)
28{
29 return inode_create (sector, entry_cnt * sizeof (struct dir_entry));
30}
31
32/* Opens and returns the directory for the given INODE, of which
33 it takes ownership. Returns a null pointer on failure. */
34struct dir *
35dir_open (struct inode *inode)
36{
37 struct dir *dir = calloc (1, sizeof *dir);
38 if (inode != NULL && dir != NULL)
39 {
40 dir->inode = inode;
41 dir->pos = 0;
42 return dir;
43 }
44 else
45 {
46 inode_close (inode);
47 free (dir);
48 return NULL;
49 }
50}
51
52/* Opens the root directory and returns a directory for it.
53 Return true if successful, false on failure. */
54struct dir *
55dir_open_root (void)
56{
57 return dir_open (inode_open (ROOT_DIR_SECTOR));
58}
59
60/* Opens and returns a new directory for the same inode as DIR.
61 Returns a null pointer on failure. */
62struct dir *
63dir_reopen (struct dir *dir)
64{
65 return dir_open (inode_reopen (dir->inode));
66}
67
68/* Destroys DIR and frees associated resources. */
69void
70dir_close (struct dir *dir)
71{
72 if (dir != NULL)
73 {
74 inode_close (dir->inode);
75 free (dir);
76 }
77}
78
79/* Returns the inode encapsulated by DIR. */
80struct inode *
81dir_get_inode (struct dir *dir)
82{
83 return dir->inode;
84}
85
86/* Searches DIR for a file with the given NAME.
87 If successful, returns true, sets *EP to the directory entry
88 if EP is non-null, and sets *OFSP to the byte offset of the
89 directory entry if OFSP is non-null.
90 otherwise, returns false and ignores EP and OFSP. */
91static bool
92lookup (const struct dir *dir, const char *name,
93 struct dir_entry *ep, off_t *ofsp)
94{
95 struct dir_entry e;
96 size_t ofs;
97
98 ASSERT (dir != NULL);
99 ASSERT (name != NULL);
100
101 for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
102 ofs += sizeof e)
103 if (e.in_use && !strcmp (name, e.name))
104 {
105 if (ep != NULL)
106 *ep = e;
107 if (ofsp != NULL)
108 *ofsp = ofs;
109 return true;
110 }
111 return false;
112}
113
114/* Searches DIR for a file with the given NAME
115 and returns true if one exists, false otherwise.
116 On success, sets *INODE to an inode for the file, otherwise to
117 a null pointer. The caller must close *INODE. */
118bool
119dir_lookup (const struct dir *dir, const char *name,
120 struct inode **inode)
121{
122 struct dir_entry e;
123
124 ASSERT (dir != NULL);
125 ASSERT (name != NULL);
126
127 if (lookup (dir, name, &e, NULL))
128 *inode = inode_open (e.inode_sector);
129 else
130 *inode = NULL;
131
132 return *inode != NULL;
133}
134
135/* Adds a file named NAME to DIR, which must not already contain a
136 file by that name. The file's inode is in sector
137 INODE_SECTOR.
138 Returns true if successful, false on failure.
139 Fails if NAME is invalid (i.e. too long) or a disk or memory
140 error occurs. */
141bool
142dir_add (struct dir *dir, const char *name, block_sector_t inode_sector)
143{
144 struct dir_entry e;
145 off_t ofs;
146 bool success = false;
147
148 ASSERT (dir != NULL);
149 ASSERT (name != NULL);
150
151 /* Check NAME for validity. */
152 if (*name == '\0' || strlen (name) > NAME_MAX)
153 return false;
154
155 /* Check that NAME is not in use. */
156 if (lookup (dir, name, NULL, NULL))
157 goto done;
158
159 /* Set OFS to offset of free slot.
160 If there are no free slots, then it will be set to the
161 current end-of-file.
162
163 inode_read_at() will only return a short read at end of file.
164 Otherwise, we'd need to verify that we didn't get a short
165 read due to something intermittent such as low memory. */
166 for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
167 ofs += sizeof e)
168 if (!e.in_use)
169 break;
170
171 /* Write slot. */
172 e.in_use = true;
173 strlcpy (e.name, name, sizeof e.name);
174 e.inode_sector = inode_sector;
175 success = inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
176
177 done:
178 return success;
179}
180
181/* Removes any entry for NAME in DIR.
182 Returns true if successful, false on failure,
183 which occurs only if there is no file with the given NAME. */
184bool
185dir_remove (struct dir *dir, const char *name)
186{
187 struct dir_entry e;
188 struct inode *inode = NULL;
189 bool success = false;
190 off_t ofs;
191
192 ASSERT (dir != NULL);
193 ASSERT (name != NULL);
194
195 /* Find directory entry. */
196 if (!lookup (dir, name, &e, &ofs))
197 goto done;
198
199 /* Open inode. */
200 inode = inode_open (e.inode_sector);
201 if (inode == NULL)
202 goto done;
203
204 /* Erase directory entry. */
205 e.in_use = false;
206 if (inode_write_at (dir->inode, &e, sizeof e, ofs) != sizeof e)
207 goto done;
208
209 /* Remove inode. */
210 inode_remove (inode);
211 success = true;
212
213 done:
214 inode_close (inode);
215 return success;
216}
217
218/* Reads the next directory entry in DIR and stores the name in
219 NAME. Returns true if successful, false if the directory
220 contains no more entries. */
221bool
222dir_readdir (struct dir *dir, char name[NAME_MAX + 1])
223{
224 struct dir_entry e;
225
226 while (inode_read_at (dir->inode, &e, sizeof e, dir->pos) == sizeof e)
227 {
228 dir->pos += sizeof e;
229 if (e.in_use)
230 {
231 strlcpy (name, e.name, NAME_MAX + 1);
232 return true;
233 }
234 }
235 return false;
236}
diff --git a/pintos-progos/filesys/directory.h b/pintos-progos/filesys/directory.h
new file mode 100644
index 0000000..930acf9
--- /dev/null
+++ b/pintos-progos/filesys/directory.h
@@ -0,0 +1,30 @@
1#ifndef FILESYS_DIRECTORY_H
2#define FILESYS_DIRECTORY_H
3
4#include <stdbool.h>
5#include <stddef.h>
6#include "devices/block.h"
7
8/* Maximum length of a file name component.
9 This is the traditional UNIX maximum length.
10 After directories are implemented, this maximum length may be
11 retained, but much longer full path names must be allowed. */
12#define NAME_MAX 14
13
14struct inode;
15
16/* Opening and closing directories. */
17bool dir_create (block_sector_t sector, size_t entry_cnt);
18struct dir *dir_open (struct inode *);
19struct dir *dir_open_root (void);
20struct dir *dir_reopen (struct dir *);
21void dir_close (struct dir *);
22struct inode *dir_get_inode (struct dir *);
23
24/* Reading and writing. */
25bool dir_lookup (const struct dir *, const char *name, struct inode **);
26bool dir_add (struct dir *, const char *name, block_sector_t);
27bool dir_remove (struct dir *, const char *name);
28bool dir_readdir (struct dir *, char name[NAME_MAX + 1]);
29
30#endif /* filesys/directory.h */
diff --git a/pintos-progos/filesys/file.c b/pintos-progos/filesys/file.c
new file mode 100644
index 0000000..d5fc10d
--- /dev/null
+++ b/pintos-progos/filesys/file.c
@@ -0,0 +1,168 @@
1#include "filesys/file.h"
2#include <debug.h>
3#include "filesys/inode.h"
4#include "threads/malloc.h"
5
6/* An open file. */
7struct file
8 {
9 struct inode *inode; /* File's inode. */
10 off_t pos; /* Current position. */
11 bool deny_write; /* Has file_deny_write() been called? */
12 };
13
14/* Opens a file for the given INODE, of which it takes ownership,
15 and returns the new file. Returns a null pointer if an
16 allocation fails or if INODE is null. */
17struct file *
18file_open (struct inode *inode)
19{
20 struct file *file = calloc (1, sizeof *file);
21 if (inode != NULL && file != NULL)
22 {
23 file->inode = inode;
24 file->pos = 0;
25 file->deny_write = false;
26 return file;
27 }
28 else
29 {
30 inode_close (inode);
31 free (file);
32 return NULL;
33 }
34}
35
36/* Opens and returns a new file for the same inode as FILE.
37 Returns a null pointer if unsuccessful. */
38struct file *
39file_reopen (struct file *file)
40{
41 return file_open (inode_reopen (file->inode));
42}
43
44/* Closes FILE. */
45void
46file_close (struct file *file)
47{
48 if (file != NULL)
49 {
50 file_allow_write (file);
51 inode_close (file->inode);
52 free (file);
53 }
54}
55
56/* Returns the inode encapsulated by FILE. */
57struct inode *
58file_get_inode (struct file *file)
59{
60 return file->inode;
61}
62
63/* Reads SIZE bytes from FILE into BUFFER,
64 starting at the file's current position.
65 Returns the number of bytes actually read,
66 which may be less than SIZE if end of file is reached.
67 Advances FILE's position by the number of bytes read. */
68off_t
69file_read (struct file *file, void *buffer, off_t size)
70{
71 off_t bytes_read = inode_read_at (file->inode, buffer, size, file->pos);
72 file->pos += bytes_read;
73 return bytes_read;
74}
75
76/* Reads SIZE bytes from FILE into BUFFER,
77 starting at offset FILE_OFS in the file.
78 Returns the number of bytes actually read,
79 which may be less than SIZE if end of file is reached.
80 The file's current position is unaffected. */
81off_t
82file_read_at (struct file *file, void *buffer, off_t size, off_t file_ofs)
83{
84 return inode_read_at (file->inode, buffer, size, file_ofs);
85}
86
87/* Writes SIZE bytes from BUFFER into FILE,
88 starting at the file's current position.
89 Returns the number of bytes actually written,
90 which may be less than SIZE if end of file is reached.
91 (Normally we'd grow the file in that case, but file growth is
92 not yet implemented.)
93 Advances FILE's position by the number of bytes read. */
94off_t
95file_write (struct file *file, const void *buffer, off_t size)
96{
97 off_t bytes_written = inode_write_at (file->inode, buffer, size, file->pos);
98 file->pos += bytes_written;
99 return bytes_written;
100}
101
102/* Writes SIZE bytes from BUFFER into FILE,
103 starting at offset FILE_OFS in the file.
104 Returns the number of bytes actually written,
105 which may be less than SIZE if end of file is reached.
106 (Normally we'd grow the file in that case, but file growth is
107 not yet implemented.)
108 The file's current position is unaffected. */
109off_t
110file_write_at (struct file *file, const void *buffer, off_t size,
111 off_t file_ofs)
112{
113 return inode_write_at (file->inode, buffer, size, file_ofs);
114}
115
116/* Prevents write operations on FILE's underlying inode
117 until file_allow_write() is called or FILE is closed. */
118void
119file_deny_write (struct file *file)
120{
121 ASSERT (file != NULL);
122 if (!file->deny_write)
123 {
124 file->deny_write = true;
125 inode_deny_write (file->inode);
126 }
127}
128
129/* Re-enables write operations on FILE's underlying inode.
130 (Writes might still be denied by some other file that has the
131 same inode open.) */
132void
133file_allow_write (struct file *file)
134{
135 ASSERT (file != NULL);
136 if (file->deny_write)
137 {
138 file->deny_write = false;
139 inode_allow_write (file->inode);
140 }
141}
142
143/* Returns the size of FILE in bytes. */
144off_t
145file_length (struct file *file)
146{
147 ASSERT (file != NULL);
148 return inode_length (file->inode);
149}
150
151/* Sets the current position in FILE to NEW_POS bytes from the
152 start of the file. */
153void
154file_seek (struct file *file, off_t new_pos)
155{
156 ASSERT (file != NULL);
157 ASSERT (new_pos >= 0);
158 file->pos = new_pos;
159}
160
161/* Returns the current position in FILE as a byte offset from the
162 start of the file. */
163off_t
164file_tell (struct file *file)
165{
166 ASSERT (file != NULL);
167 return file->pos;
168}
diff --git a/pintos-progos/filesys/file.h b/pintos-progos/filesys/file.h
new file mode 100644
index 0000000..a33c5af
--- /dev/null
+++ b/pintos-progos/filesys/file.h
@@ -0,0 +1,29 @@
1#ifndef FILESYS_FILE_H
2#define FILESYS_FILE_H
3
4#include "filesys/off_t.h"
5
6struct inode;
7
8/* Opening and closing files. */
9struct file *file_open (struct inode *);
10struct file *file_reopen (struct file *);
11void file_close (struct file *);
12struct inode *file_get_inode (struct file *);
13
14/* Reading and writing. */
15off_t file_read (struct file *, void *, off_t);
16off_t file_read_at (struct file *, void *, off_t size, off_t start);
17off_t file_write (struct file *, const void *, off_t);
18off_t file_write_at (struct file *, const void *, off_t size, off_t start);
19
20/* Preventing writes. */
21void file_deny_write (struct file *);
22void file_allow_write (struct file *);
23
24/* File position. */
25void file_seek (struct file *, off_t);
26off_t file_tell (struct file *);
27off_t file_length (struct file *);
28
29#endif /* filesys/file.h */
diff --git a/pintos-progos/filesys/filesys.c b/pintos-progos/filesys/filesys.c
new file mode 100644
index 0000000..7a53f5f
--- /dev/null
+++ b/pintos-progos/filesys/filesys.c
@@ -0,0 +1,103 @@
1#include "filesys/filesys.h"
2#include <debug.h>
3#include <stdio.h>
4#include <string.h>
5#include "filesys/file.h"
6#include "filesys/free-map.h"
7#include "filesys/inode.h"
8#include "filesys/directory.h"
9
10/* Partition that contains the file system. */
11struct block *fs_device;
12
13static void do_format (void);
14
15/* Initializes the file system module.
16 If FORMAT is true, reformats the file system. */
17void
18filesys_init (bool format)
19{
20 fs_device = block_get_role (BLOCK_FILESYS);
21 if (fs_device == NULL)
22 PANIC ("No file system device found, can't initialize file system.");
23
24 inode_init ();
25 free_map_init ();
26
27 if (format)
28 do_format ();
29
30 free_map_open ();
31}
32
33/* Shuts down the file system module, writing any unwritten data
34 to disk. */
35void
36filesys_done (void)
37{
38 free_map_close ();
39}
40
41/* Creates a file named NAME with the given INITIAL_SIZE.
42 Returns true if successful, false otherwise.
43 Fails if a file named NAME already exists,
44 or if internal memory allocation fails. */
45bool
46filesys_create (const char *name, off_t initial_size)
47{
48 block_sector_t inode_sector = 0;
49 struct dir *dir = dir_open_root ();
50 bool success = (dir != NULL
51 && free_map_allocate (1, &inode_sector)
52 && inode_create (inode_sector, initial_size)
53 && dir_add (dir, name, inode_sector));
54 if (!success && inode_sector != 0)
55 free_map_release (inode_sector, 1);
56 dir_close (dir);
57
58 return success;
59}
60
61/* Opens the file with the given NAME.
62 Returns the new file if successful or a null pointer
63 otherwise.
64 Fails if no file named NAME exists,
65 or if an internal memory allocation fails. */
66struct file *
67filesys_open (const char *name)
68{
69 struct dir *dir = dir_open_root ();
70 struct inode *inode = NULL;
71
72 if (dir != NULL)
73 dir_lookup (dir, name, &inode);
74 dir_close (dir);
75
76 return file_open (inode);
77}
78
79/* Deletes the file named NAME.
80 Returns true if successful, false on failure.
81 Fails if no file named NAME exists,
82 or if an internal memory allocation fails. */
83bool
84filesys_remove (const char *name)
85{
86 struct dir *dir = dir_open_root ();
87 bool success = dir != NULL && dir_remove (dir, name);
88 dir_close (dir);
89
90 return success;
91}
92
93/* Formats the file system. */
94static void
95do_format (void)
96{
97 printf ("Formatting file system...");
98 free_map_create ();
99 if (!dir_create (ROOT_DIR_SECTOR, 16))
100 PANIC ("root directory creation failed");
101 free_map_close ();
102 printf ("done.\n");
103}
diff --git a/pintos-progos/filesys/filesys.h b/pintos-progos/filesys/filesys.h
new file mode 100644
index 0000000..c1cda84
--- /dev/null
+++ b/pintos-progos/filesys/filesys.h
@@ -0,0 +1,20 @@
1#ifndef FILESYS_FILESYS_H
2#define FILESYS_FILESYS_H
3
4#include <stdbool.h>
5#include "filesys/off_t.h"
6
7/* Sectors of system file inodes. */
8#define FREE_MAP_SECTOR 0 /* Free map file inode sector. */
9#define ROOT_DIR_SECTOR 1 /* Root directory file inode sector. */
10
11/* Block device that contains the file system. */
12struct block *fs_device;
13
14void filesys_init (bool format);
15void filesys_done (void);
16bool filesys_create (const char *name, off_t initial_size);
17struct file *filesys_open (const char *name);
18bool filesys_remove (const char *name);
19
20#endif /* filesys/filesys.h */
diff --git a/pintos-progos/filesys/free-map.c b/pintos-progos/filesys/free-map.c
new file mode 100644
index 0000000..29ea4df
--- /dev/null
+++ b/pintos-progos/filesys/free-map.c
@@ -0,0 +1,85 @@
1#include "filesys/free-map.h"
2#include <bitmap.h>
3#include <debug.h>
4#include "filesys/file.h"
5#include "filesys/filesys.h"
6#include "filesys/inode.h"
7
8static struct file *free_map_file; /* Free map file. */
9static struct bitmap *free_map; /* Free map, one bit per sector. */
10
11/* Initializes the free map. */
12void
13free_map_init (void)
14{
15 free_map = bitmap_create (block_size (fs_device));
16 if (free_map == NULL)
17 PANIC ("bitmap creation failed--file system device is too large");
18 bitmap_mark (free_map, FREE_MAP_SECTOR);
19 bitmap_mark (free_map, ROOT_DIR_SECTOR);
20}
21
22/* Allocates CNT consecutive sectors from the free map and stores
23 the first into *SECTORP.
24 Returns true if successful, false if not enough consecutive
25 sectors were available or if the free_map file could not be
26 written. */
27bool
28free_map_allocate (size_t cnt, block_sector_t *sectorp)
29{
30 block_sector_t sector = bitmap_scan_and_flip (free_map, 0, cnt, false);
31 if (sector != BITMAP_ERROR
32 && free_map_file != NULL
33 && !bitmap_write (free_map, free_map_file))
34 {
35 bitmap_set_multiple (free_map, sector, cnt, false);
36 sector = BITMAP_ERROR;
37 }
38 if (sector != BITMAP_ERROR)
39 *sectorp = sector;
40 return sector != BITMAP_ERROR;
41}
42
43/* Makes CNT sectors starting at SECTOR available for use. */
44void
45free_map_release (block_sector_t sector, size_t cnt)
46{
47 ASSERT (bitmap_all (free_map, sector, cnt));
48 bitmap_set_multiple (free_map, sector, cnt, false);
49 bitmap_write (free_map, free_map_file);
50}
51
52/* Opens the free map file and reads it from disk. */
53void
54free_map_open (void)
55{
56 free_map_file = file_open (inode_open (FREE_MAP_SECTOR));
57 if (free_map_file == NULL)
58 PANIC ("can't open free map");
59 if (!bitmap_read (free_map, free_map_file))
60 PANIC ("can't read free map");
61}
62
63/* Writes the free map to disk and closes the free map file. */
64void
65free_map_close (void)
66{
67 file_close (free_map_file);
68}
69
70/* Creates a new free map file on disk and writes the free map to
71 it. */
72void
73free_map_create (void)
74{
75 /* Create inode. */
76 if (!inode_create (FREE_MAP_SECTOR, bitmap_file_size (free_map)))
77 PANIC ("free map creation failed");
78
79 /* Write bitmap to file. */
80 free_map_file = file_open (inode_open (FREE_MAP_SECTOR));
81 if (free_map_file == NULL)
82 PANIC ("can't open free map");
83 if (!bitmap_write (free_map, free_map_file))
84 PANIC ("can't write free map");
85}
diff --git a/pintos-progos/filesys/free-map.h b/pintos-progos/filesys/free-map.h
new file mode 100644
index 0000000..316cd1c
--- /dev/null
+++ b/pintos-progos/filesys/free-map.h
@@ -0,0 +1,17 @@
1#ifndef FILESYS_FREE_MAP_H
2#define FILESYS_FREE_MAP_H
3
4#include <stdbool.h>
5#include <stddef.h>
6#include "devices/block.h"
7
8void free_map_init (void);
9void free_map_read (void);
10void free_map_create (void);
11void free_map_open (void);
12void free_map_close (void);
13
14bool free_map_allocate (size_t, block_sector_t *);
15void free_map_release (block_sector_t, size_t);
16
17#endif /* filesys/free-map.h */
diff --git a/pintos-progos/filesys/fsutil.c b/pintos-progos/filesys/fsutil.c
new file mode 100644
index 0000000..447f291
--- /dev/null
+++ b/pintos-progos/filesys/fsutil.c
@@ -0,0 +1,222 @@
1#include "filesys/fsutil.h"
2#include <debug.h>
3#include <stdio.h>
4#include <stdlib.h>
5#include <string.h>
6#include <ustar.h>
7#include "filesys/directory.h"
8#include "filesys/file.h"
9#include "filesys/filesys.h"
10#include "threads/malloc.h"
11#include "threads/palloc.h"
12#include "threads/vaddr.h"
13
14/* List files in the root directory. */
15void
16fsutil_ls (char **argv UNUSED)
17{
18 struct dir *dir;
19 char name[NAME_MAX + 1];
20
21 printf ("Files in the root directory:\n");
22 dir = dir_open_root ();
23 if (dir == NULL)
24 PANIC ("root dir open failed");
25 while (dir_readdir (dir, name))
26 printf ("%s\n", name);
27 printf ("End of listing.\n");
28}
29
30/* Prints the contents of file ARGV[1] to the system console as
31 hex and ASCII. */
32void
33fsutil_cat (char **argv)
34{
35 const char *file_name = argv[1];
36
37 struct file *file;
38 char *buffer;
39
40 printf ("Printing '%s' to the console...\n", file_name);
41 file = filesys_open (file_name);
42 if (file == NULL)
43 PANIC ("%s: open failed", file_name);
44 buffer = palloc_get_page (PAL_ASSERT);
45 for (;;)
46 {
47 off_t pos = file_tell (file);
48 off_t n = file_read (file, buffer, PGSIZE);
49 if (n == 0)
50 break;
51
52 hex_dump (pos, buffer, n, true);
53 }
54 palloc_free_page (buffer);
55 file_close (file);
56}
57
58/* Deletes file ARGV[1]. */
59void
60fsutil_rm (char **argv)
61{
62 const char *file_name = argv[1];
63
64 printf ("Deleting '%s'...\n", file_name);
65 if (!filesys_remove (file_name))
66 PANIC ("%s: delete failed\n", file_name);
67}
68
69/* Extracts a ustar-format tar archive from the scratch block
70 device into the Pintos file system. */
71void
72fsutil_extract (char **argv UNUSED)
73{
74 static block_sector_t sector = 0;
75
76 struct block *src;
77 void *header, *data;
78
79 /* Allocate buffers. */
80 header = malloc (BLOCK_SECTOR_SIZE);
81 data = malloc (BLOCK_SECTOR_SIZE);
82 if (header == NULL || data == NULL)
83 PANIC ("couldn't allocate buffers");
84
85 /* Open source block device. */
86 src = block_get_role (BLOCK_SCRATCH);
87 if (src == NULL)
88 PANIC ("couldn't open scratch device");
89
90 printf ("Extracting ustar archive from scratch device "
91 "into file system...\n");
92
93 for (;;)
94 {
95 const char *file_name;
96 const char *error;
97 enum ustar_type type;
98 int size;
99
100 /* Read and parse ustar header. */
101 block_read (src, sector++, header);
102 error = ustar_parse_header (header, &file_name, &type, &size);
103 if (error != NULL)
104 PANIC ("bad ustar header in sector %"PRDSNu" (%s)", sector - 1, error);
105
106 if (type == USTAR_EOF)
107 {
108 /* End of archive. */
109 break;
110 }
111 else if (type == USTAR_DIRECTORY)
112 printf ("ignoring directory %s\n", file_name);
113 else if (type == USTAR_REGULAR)
114 {
115 struct file *dst;
116
117 printf ("Putting '%s' into the file system...\n", file_name);
118
119 /* Create destination file. */
120 if (!filesys_create (file_name, size))
121 PANIC ("%s: create failed", file_name);
122 dst = filesys_open (file_name);
123 if (dst == NULL)
124 PANIC ("%s: open failed", file_name);
125
126 /* Do copy. */
127 while (size > 0)
128 {
129 int chunk_size = (size > BLOCK_SECTOR_SIZE
130 ? BLOCK_SECTOR_SIZE
131 : size);
132 block_read (src, sector++, data);
133 if (file_write (dst, data, chunk_size) != chunk_size)
134 PANIC ("%s: write failed with %d bytes unwritten",
135 file_name, size);
136 size -= chunk_size;
137 }
138
139 /* Finish up. */
140 file_close (dst);
141 }
142 }
143
144 /* Erase the ustar header from the start of the block device,
145 so that the extraction operation is idempotent. We erase
146 two blocks because two blocks of zeros are the ustar
147 end-of-archive marker. */
148 printf ("Erasing ustar archive...\n");
149 memset (header, 0, BLOCK_SECTOR_SIZE);
150 block_write (src, 0, header);
151 block_write (src, 1, header);
152
153 free (data);
154 free (header);
155}
156
157/* Copies file FILE_NAME from the file system to the scratch
158 device, in ustar format.
159
160 The first call to this function will write starting at the
161 beginning of the scratch device. Later calls advance across
162 the device. This position is independent of that used for
163 fsutil_extract(), so `extract' should precede all
164 `append's. */
165void
166fsutil_append (char **argv)
167{
168 static block_sector_t sector = 0;
169
170 const char *file_name = argv[1];
171 void *buffer;
172 struct file *src;
173 struct block *dst;
174 off_t size;
175
176 printf ("Appending '%s' to ustar archive on scratch device...\n", file_name);
177
178 /* Allocate buffer. */
179 buffer = malloc (BLOCK_SECTOR_SIZE);
180 if (buffer == NULL)
181 PANIC ("couldn't allocate buffer");
182
183 /* Open source file. */
184 src = filesys_open (file_name);
185 if (src == NULL)
186 PANIC ("%s: open failed", file_name);
187 size = file_length (src);
188
189 /* Open target block device. */
190 dst = block_get_role (BLOCK_SCRATCH);
191 if (dst == NULL)
192 PANIC ("couldn't open scratch device");
193
194 /* Write ustar header to first sector. */
195 if (!ustar_make_header (file_name, USTAR_REGULAR, size, buffer))
196 PANIC ("%s: name too long for ustar format", file_name);
197 block_write (dst, sector++, buffer);
198
199 /* Do copy. */
200 while (size > 0)
201 {
202 int chunk_size = size > BLOCK_SECTOR_SIZE ? BLOCK_SECTOR_SIZE : size;
203 if (sector >= block_size (dst))
204 PANIC ("%s: out of space on scratch device", file_name);
205 if (file_read (src, buffer, chunk_size) != chunk_size)
206 PANIC ("%s: read failed with %"PROTd" bytes unread", file_name, size);
207 memset (buffer + chunk_size, 0, BLOCK_SECTOR_SIZE - chunk_size);
208 block_write (dst, sector++, buffer);
209 size -= chunk_size;
210 }
211
212 /* Write ustar end-of-archive marker, which is two consecutive
213 sectors full of zeros. Don't advance our position past
214 them, though, in case we have more files to append. */
215 memset (buffer, 0, BLOCK_SECTOR_SIZE);
216 block_write (dst, sector, buffer);
217 block_write (dst, sector, buffer + 1);
218
219 /* Finish up. */
220 file_close (src);
221 free (buffer);
222}
diff --git a/pintos-progos/filesys/fsutil.h b/pintos-progos/filesys/fsutil.h
new file mode 100644
index 0000000..cc73705
--- /dev/null
+++ b/pintos-progos/filesys/fsutil.h
@@ -0,0 +1,10 @@
1#ifndef FILESYS_FSUTIL_H
2#define FILESYS_FSUTIL_H
3
4void fsutil_ls (char **argv);
5void fsutil_cat (char **argv);
6void fsutil_rm (char **argv);
7void fsutil_extract (char **argv);
8void fsutil_append (char **argv);
9
10#endif /* filesys/fsutil.h */
diff --git a/pintos-progos/filesys/inode.c b/pintos-progos/filesys/inode.c
new file mode 100644
index 0000000..3463563
--- /dev/null
+++ b/pintos-progos/filesys/inode.c
@@ -0,0 +1,345 @@
1#include "filesys/inode.h"
2#include <list.h>
3#include <debug.h>
4#include <round.h>
5#include <string.h>
6#include "filesys/filesys.h"
7#include "filesys/free-map.h"
8#include "threads/malloc.h"
9
10/* Identifies an inode. */
11#define INODE_MAGIC 0x494e4f44
12
13/* On-disk inode.
14 Must be exactly BLOCK_SECTOR_SIZE bytes long. */
15struct inode_disk
16 {
17 block_sector_t start; /* First data sector. */
18 off_t length; /* File size in bytes. */
19 unsigned magic; /* Magic number. */
20 uint32_t unused[125]; /* Not used. */
21 };
22
23/* Returns the number of sectors to allocate for an inode SIZE
24 bytes long. */
25static inline size_t
26bytes_to_sectors (off_t size)
27{
28 return DIV_ROUND_UP (size, BLOCK_SECTOR_SIZE);
29}
30
31/* In-memory inode. */
32struct inode
33 {
34 struct list_elem elem; /* Element in inode list. */
35 block_sector_t sector; /* Sector number of disk location. */
36 int open_cnt; /* Number of openers. */
37 bool removed; /* True if deleted, false otherwise. */
38 int deny_write_cnt; /* 0: writes ok, >0: deny writes. */
39 struct inode_disk data; /* Inode content. */
40 };
41
42/* Returns the block device sector that contains byte offset POS
43 within INODE.
44 Returns -1 if INODE does not contain data for a byte at offset
45 POS. */
46static block_sector_t
47byte_to_sector (const struct inode *inode, off_t pos)
48{
49 ASSERT (inode != NULL);
50 if (pos < inode->data.length)
51 return inode->data.start + pos / BLOCK_SECTOR_SIZE;
52 else
53 return -1;
54}
55
56/* List of open inodes, so that opening a single inode twice
57 returns the same `struct inode'. */
58static struct list open_inodes;
59
60/* Initializes the inode module. */
61void
62inode_init (void)
63{
64 list_init (&open_inodes);
65}
66
67/* Initializes an inode with LENGTH bytes of data and
68 writes the new inode to sector SECTOR on the file system
69 device.
70 Returns true if successful.
71 Returns false if memory or disk allocation fails. */
72bool
73inode_create (block_sector_t sector, off_t length)
74{
75 struct inode_disk *disk_inode = NULL;
76 bool success = false;
77
78 ASSERT (length >= 0);
79
80 /* If this assertion fails, the inode structure is not exactly
81 one sector in size, and you should fix that. */
82 ASSERT (sizeof *disk_inode == BLOCK_SECTOR_SIZE);
83
84 disk_inode = calloc (1, sizeof *disk_inode);
85 if (disk_inode != NULL)
86 {
87 size_t sectors = bytes_to_sectors (length);
88 disk_inode->length = length;
89 disk_inode->magic = INODE_MAGIC;
90 if (free_map_allocate (sectors, &disk_inode->start))
91 {
92 block_write (fs_device, sector, disk_inode);
93 if (sectors > 0)
94 {
95 static char zeros[BLOCK_SECTOR_SIZE];
96 size_t i;
97
98 for (i = 0; i < sectors; i++)
99 block_write (fs_device, disk_inode->start + i, zeros);
100 }
101 success = true;
102 }
103 free (disk_inode);
104 }
105 return success;
106}
107
108/* Reads an inode from SECTOR
109 and returns a `struct inode' that contains it.
110 Returns a null pointer if memory allocation fails. */
111struct inode *
112inode_open (block_sector_t sector)
113{
114 struct list_elem *e;
115 struct inode *inode;
116
117 /* Check whether this inode is already open. */
118 for (e = list_begin (&open_inodes); e != list_end (&open_inodes);
119 e = list_next (e))
120 {
121 inode = list_entry (e, struct inode, elem);
122 if (inode->sector == sector)
123 {
124 inode_reopen (inode);
125 return inode;
126 }
127 }
128
129 /* Allocate memory. */
130 inode = malloc (sizeof *inode);
131 if (inode == NULL)
132 return NULL;
133
134 /* Initialize. */
135 list_push_front (&open_inodes, &inode->elem);
136 inode->sector = sector;
137 inode->open_cnt = 1;
138 inode->deny_write_cnt = 0;
139 inode->removed = false;
140 block_read (fs_device, inode->sector, &inode->data);
141 return inode;
142}
143
144/* Reopens and returns INODE. */
145struct inode *
146inode_reopen (struct inode *inode)
147{
148 if (inode != NULL)
149 inode->open_cnt++;
150 return inode;
151}
152
153/* Returns INODE's inode number. */
154block_sector_t
155inode_get_inumber (const struct inode *inode)
156{
157 return inode->sector;
158}
159
160/* Closes INODE and writes it to disk.
161 If this was the last reference to INODE, frees its memory.
162 If INODE was also a removed inode, frees its blocks. */
163void
164inode_close (struct inode *inode)
165{
166 /* Ignore null pointer. */
167 if (inode == NULL)
168 return;
169
170 /* Release resources if this was the last opener. */
171 if (--inode->open_cnt == 0)
172 {
173 /* Remove from inode list and release lock. */
174 list_remove (&inode->elem);
175
176 /* Deallocate blocks if removed. */
177 if (inode->removed)
178 {
179 free_map_release (inode->sector, 1);
180 free_map_release (inode->data.start,
181 bytes_to_sectors (inode->data.length));
182 }
183
184 free (inode);
185 }
186}
187
188/* Marks INODE to be deleted when it is closed by the last caller who
189 has it open. */
190void
191inode_remove (struct inode *inode)
192{
193 ASSERT (inode != NULL);
194 inode->removed = true;
195}
196
197/* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET.
198 Returns the number of bytes actually read, which may be less
199 than SIZE if an error occurs or end of file is reached. */
200off_t
201inode_read_at (struct inode *inode, void *buffer_, off_t size, off_t offset)
202{
203 uint8_t *buffer = buffer_;
204 off_t bytes_read = 0;
205 uint8_t *bounce = NULL;
206
207 while (size > 0)
208 {
209 /* Disk sector to read, starting byte offset within sector. */
210 block_sector_t sector_idx = byte_to_sector (inode, offset);
211 int sector_ofs = offset % BLOCK_SECTOR_SIZE;
212
213 /* Bytes left in inode, bytes left in sector, lesser of the two. */
214 off_t inode_left = inode_length (inode) - offset;
215 int sector_left = BLOCK_SECTOR_SIZE - sector_ofs;
216 int min_left = inode_left < sector_left ? inode_left : sector_left;
217
218 /* Number of bytes to actually copy out of this sector. */
219 int chunk_size = size < min_left ? size : min_left;
220 if (chunk_size <= 0)
221 break;
222
223 if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE)
224 {
225 /* Read full sector directly into caller's buffer. */
226 block_read (fs_device, sector_idx, buffer + bytes_read);
227 }
228 else
229 {
230 /* Read sector into bounce buffer, then partially copy
231 into caller's buffer. */
232 if (bounce == NULL)
233 {
234 bounce = malloc (BLOCK_SECTOR_SIZE);
235 if (bounce == NULL)
236 break;
237 }
238 block_read (fs_device, sector_idx, bounce);
239 memcpy (buffer + bytes_read, bounce + sector_ofs, chunk_size);
240 }
241
242 /* Advance. */
243 size -= chunk_size;
244 offset += chunk_size;
245 bytes_read += chunk_size;
246 }
247 free (bounce);
248
249 return bytes_read;
250}
251
252/* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET.
253 Returns the number of bytes actually written, which may be
254 less than SIZE if end of file is reached or an error occurs.
255 (Normally a write at end of file would extend the inode, but
256 growth is not yet implemented.) */
257off_t
258inode_write_at (struct inode *inode, const void *buffer_, off_t size,
259 off_t offset)
260{
261 const uint8_t *buffer = buffer_;
262 off_t bytes_written = 0;
263 uint8_t *bounce = NULL;
264
265 if (inode->deny_write_cnt)
266 return 0;
267
268 while (size > 0)
269 {
270 /* Sector to write, starting byte offset within sector. */
271 block_sector_t sector_idx = byte_to_sector (inode, offset);
272 int sector_ofs = offset % BLOCK_SECTOR_SIZE;
273
274 /* Bytes left in inode, bytes left in sector, lesser of the two. */
275 off_t inode_left = inode_length (inode) - offset;
276 int sector_left = BLOCK_SECTOR_SIZE - sector_ofs;
277 int min_left = inode_left < sector_left ? inode_left : sector_left;
278
279 /* Number of bytes to actually write into this sector. */
280 int chunk_size = size < min_left ? size : min_left;
281 if (chunk_size <= 0)
282 break;
283
284 if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE)
285 {
286 /* Write full sector directly to disk. */
287 block_write (fs_device, sector_idx, buffer + bytes_written);
288 }
289 else
290 {
291 /* We need a bounce buffer. */
292 if (bounce == NULL)
293 {
294 bounce = malloc (BLOCK_SECTOR_SIZE);
295 if (bounce == NULL)
296 break;
297 }
298
299 /* If the sector contains data before or after the chunk
300 we're writing, then we need to read in the sector
301 first. Otherwise we start with a sector of all zeros. */
302 if (sector_ofs > 0 || chunk_size < sector_left)
303 block_read (fs_device, sector_idx, bounce);
304 else
305 memset (bounce, 0, BLOCK_SECTOR_SIZE);
306 memcpy (bounce + sector_ofs, buffer + bytes_written, chunk_size);
307 block_write (fs_device, sector_idx, bounce);
308 }
309
310 /* Advance. */
311 size -= chunk_size;
312 offset += chunk_size;
313 bytes_written += chunk_size;
314 }
315 free (bounce);
316
317 return bytes_written;
318}
319
320/* Disables writes to INODE.
321 May be called at most once per inode opener. */
322void
323inode_deny_write (struct inode *inode)
324{
325 inode->deny_write_cnt++;
326 ASSERT (inode->deny_write_cnt <= inode->open_cnt);
327}
328
329/* Re-enables writes to INODE.
330 Must be called once by each inode opener who has called
331 inode_deny_write() on the inode, before closing the inode. */
332void
333inode_allow_write (struct inode *inode)
334{
335 ASSERT (inode->deny_write_cnt > 0);
336 ASSERT (inode->deny_write_cnt <= inode->open_cnt);
337 inode->deny_write_cnt--;
338}
339
340/* Returns the length, in bytes, of INODE's data. */
341off_t
342inode_length (const struct inode *inode)
343{
344 return inode->data.length;
345}
diff --git a/pintos-progos/filesys/inode.h b/pintos-progos/filesys/inode.h
new file mode 100644
index 0000000..cb42310
--- /dev/null
+++ b/pintos-progos/filesys/inode.h
@@ -0,0 +1,23 @@
1#ifndef FILESYS_INODE_H
2#define FILESYS_INODE_H
3
4#include <stdbool.h>
5#include "filesys/off_t.h"
6#include "devices/block.h"
7
8struct bitmap;
9
10void inode_init (void);
11bool inode_create (block_sector_t, off_t);
12struct inode *inode_open (block_sector_t);
13struct inode *inode_reopen (struct inode *);
14block_sector_t inode_get_inumber (const struct inode *);
15void inode_close (struct inode *);
16void inode_remove (struct inode *);
17off_t inode_read_at (struct inode *, void *, off_t size, off_t offset);
18off_t inode_write_at (struct inode *, const void *, off_t size, off_t offset);
19void inode_deny_write (struct inode *);
20void inode_allow_write (struct inode *);
21off_t inode_length (const struct inode *);
22
23#endif /* filesys/inode.h */
diff --git a/pintos-progos/filesys/off_t.h b/pintos-progos/filesys/off_t.h
new file mode 100644
index 0000000..9caff4d
--- /dev/null
+++ b/pintos-progos/filesys/off_t.h
@@ -0,0 +1,15 @@
1#ifndef FILESYS_OFF_T_H
2#define FILESYS_OFF_T_H
3
4#include <stdint.h>
5
6/* An offset within a file.
7 This is a separate header because multiple headers want this
8 definition but not any others. */
9typedef int32_t off_t;
10
11/* Format specifier for printf(), e.g.:
12 printf ("offset=%"PROTd"\n", offset); */
13#define PROTd PRId32
14
15#endif /* filesys/off_t.h */
diff --git a/pintos-progos/intro/Make.vars b/pintos-progos/intro/Make.vars
new file mode 100644
index 0000000..c612275
--- /dev/null
+++ b/pintos-progos/intro/Make.vars
@@ -0,0 +1,7 @@
1# -*- makefile -*-
2
3kernel.bin: DEFINES = -DUSERPROG -DFILESYS
4KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys $(KERNEL_TESTS)
5KERNEL_TESTS = tests/intro/alarm-clock
6TEST_SUBDIRS = tests/intro/alarm-clock tests/intro/userprog-args
7GRADING_FILE = $(SRCDIR)/tests/intro/Grading
diff --git a/pintos-progos/intro/Makefile b/pintos-progos/intro/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/pintos-progos/intro/Makefile
@@ -0,0 +1 @@
include ../Makefile.kernel
diff --git a/pintos-progos/lib/arithmetic.c b/pintos-progos/lib/arithmetic.c
new file mode 100644
index 0000000..bfc9b5a
--- /dev/null
+++ b/pintos-progos/lib/arithmetic.c
@@ -0,0 +1,189 @@
1#include <stdint.h>
2
3/* On x86, division of one 64-bit integer by another cannot be
4 done with a single instruction or a short sequence. Thus, GCC
5 implements 64-bit division and remainder operations through
6 function calls. These functions are normally obtained from
7 libgcc, which is automatically included by GCC in any link
8 that it does.
9
10 Some x86-64 machines, however, have a compiler and utilities
11 that can generate 32-bit x86 code without having any of the
12 necessary libraries, including libgcc. Thus, we can make
13 Pintos work on these machines by simply implementing our own
14 64-bit division routines, which are the only routines from
15 libgcc that Pintos requires.
16
17 Completeness is another reason to include these routines. If
18 Pintos is completely self-contained, then that makes it that
19 much less mysterious. */
20
21/* Uses x86 DIVL instruction to divide 64-bit N by 32-bit D to
22 yield a 32-bit quotient. Returns the quotient.
23 Traps with a divide error (#DE) if the quotient does not fit
24 in 32 bits. */
25static inline uint32_t
26divl (uint64_t n, uint32_t d)
27{
28 uint32_t n1 = n >> 32;
29 uint32_t n0 = n;
30 uint32_t q, r;
31
32 asm ("divl %4"
33 : "=d" (r), "=a" (q)
34 : "0" (n1), "1" (n0), "rm" (d));
35
36 return q;
37}
38
39/* Returns the number of leading zero bits in X,
40 which must be nonzero. */
41static int
42nlz (uint32_t x)
43{
44 /* This technique is portable, but there are better ways to do
45 it on particular systems. With sufficiently new enough GCC,
46 you can use __builtin_clz() to take advantage of GCC's
47 knowledge of how to do it. Or you can use the x86 BSR
48 instruction directly. */
49 int n = 0;
50 if (x <= 0x0000FFFF)
51 {
52 n += 16;
53 x <<= 16;
54 }
55 if (x <= 0x00FFFFFF)
56 {
57 n += 8;
58 x <<= 8;
59 }
60 if (x <= 0x0FFFFFFF)
61 {
62 n += 4;
63 x <<= 4;
64 }
65 if (x <= 0x3FFFFFFF)
66 {
67 n += 2;
68 x <<= 2;
69 }
70 if (x <= 0x7FFFFFFF)
71 n++;
72 return n;
73}
74
75/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the
76 quotient. */
77static uint64_t
78udiv64 (uint64_t n, uint64_t d)
79{
80 if ((d >> 32) == 0)
81 {
82 /* Proof of correctness:
83
84 Let n, d, b, n1, and n0 be defined as in this function.
85 Let [x] be the "floor" of x. Let T = b[n1/d]. Assume d
86 nonzero. Then:
87 [n/d] = [n/d] - T + T
88 = [n/d - T] + T by (1) below
89 = [(b*n1 + n0)/d - T] + T by definition of n
90 = [(b*n1 + n0)/d - dT/d] + T
91 = [(b(n1 - d[n1/d]) + n0)/d] + T
92 = [(b[n1 % d] + n0)/d] + T, by definition of %
93 which is the expression calculated below.
94
95 (1) Note that for any real x, integer i: [x] + i = [x + i].
96
97 To prevent divl() from trapping, [(b[n1 % d] + n0)/d] must
98 be less than b. Assume that [n1 % d] and n0 take their
99 respective maximum values of d - 1 and b - 1:
100 [(b(d - 1) + (b - 1))/d] < b
101 <=> [(bd - 1)/d] < b
102 <=> [b - 1/d] < b
103 which is a tautology.
104
105 Therefore, this code is correct and will not trap. */
106 uint64_t b = 1ULL << 32;
107 uint32_t n1 = n >> 32;
108 uint32_t n0 = n;
109 uint32_t d0 = d;
110
111 return divl (b * (n1 % d0) + n0, d0) + b * (n1 / d0);
112 }
113 else
114 {
115 /* Based on the algorithm and proof available from
116 http://www.hackersdelight.org/revisions.pdf. */
117 if (n < d)
118 return 0;
119 else
120 {
121 uint32_t d1 = d >> 32;
122 int s = nlz (d1);
123 uint64_t q = divl (n >> 1, (d << s) >> 32) >> (31 - s);
124 return n - (q - 1) * d < d ? q - 1 : q;
125 }
126 }
127}
128
129/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the
130 remainder. */
131static uint32_t
132umod64 (uint64_t n, uint64_t d)
133{
134 return n - d * udiv64 (n, d);
135}
136
137/* Divides signed 64-bit N by signed 64-bit D and returns the
138 quotient. */
139static int64_t
140sdiv64 (int64_t n, int64_t d)
141{
142 uint64_t n_abs = n >= 0 ? (uint64_t) n : -(uint64_t) n;
143 uint64_t d_abs = d >= 0 ? (uint64_t) d : -(uint64_t) d;
144 uint64_t q_abs = udiv64 (n_abs, d_abs);
145 return (n < 0) == (d < 0) ? (int64_t) q_abs : -(int64_t) q_abs;
146}
147
148/* Divides signed 64-bit N by signed 64-bit D and returns the
149 remainder. */
150static int32_t
151smod64 (int64_t n, int64_t d)
152{
153 return n - d * sdiv64 (n, d);
154}
155
156/* These are the routines that GCC calls. */
157
158long long __divdi3 (long long n, long long d);
159long long __moddi3 (long long n, long long d);
160unsigned long long __udivdi3 (unsigned long long n, unsigned long long d);
161unsigned long long __umoddi3 (unsigned long long n, unsigned long long d);
162
163/* Signed 64-bit division. */
164long long
165__divdi3 (long long n, long long d)
166{
167 return sdiv64 (n, d);
168}
169
170/* Signed 64-bit remainder. */
171long long
172__moddi3 (long long n, long long d)
173{
174 return smod64 (n, d);
175}
176
177/* Unsigned 64-bit division. */
178unsigned long long
179__udivdi3 (unsigned long long n, unsigned long long d)
180{
181 return udiv64 (n, d);
182}
183
184/* Unsigned 64-bit remainder. */
185unsigned long long
186__umoddi3 (unsigned long long n, unsigned long long d)
187{
188 return umod64 (n, d);
189}
diff --git a/pintos-progos/lib/ctype.h b/pintos-progos/lib/ctype.h
new file mode 100644
index 0000000..9096aca
--- /dev/null
+++ b/pintos-progos/lib/ctype.h
@@ -0,0 +1,28 @@
1#ifndef __LIB_CTYPE_H
2#define __LIB_CTYPE_H
3
4static inline int islower (int c) { return c >= 'a' && c <= 'z'; }
5static inline int isupper (int c) { return c >= 'A' && c <= 'Z'; }
6static inline int isalpha (int c) { return islower (c) || isupper (c); }
7static inline int isdigit (int c) { return c >= '0' && c <= '9'; }
8static inline int isalnum (int c) { return isalpha (c) || isdigit (c); }
9static inline int isxdigit (int c) {
10 return isdigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
11}
12static inline int isspace (int c) {
13 return (c == ' ' || c == '\f' || c == '\n'
14 || c == '\r' || c == '\t' || c == '\v');
15}
16static inline int isblank (int c) { return c == ' ' || c == '\t'; }
17static inline int isgraph (int c) { return c > 32 && c < 127; }
18static inline int isprint (int c) { return c >= 32 && c < 127; }
19static inline int iscntrl (int c) { return (c >= 0 && c < 32) || c == 127; }
20static inline int isascii (int c) { return c >= 0 && c < 128; }
21static inline int ispunct (int c) {
22 return isprint (c) && !isalnum (c) && !isspace (c);
23}
24
25static inline int tolower (int c) { return isupper (c) ? c - 'A' + 'a' : c; }
26static inline int toupper (int c) { return islower (c) ? c - 'a' + 'A' : c; }
27
28#endif /* lib/ctype.h */
diff --git a/pintos-progos/lib/debug.c b/pintos-progos/lib/debug.c
new file mode 100644
index 0000000..b4f8c2d
--- /dev/null
+++ b/pintos-progos/lib/debug.c
@@ -0,0 +1,32 @@
1#include <debug.h>
2#include <stdarg.h>
3#include <stdbool.h>
4#include <stddef.h>
5#include <stdio.h>
6#include <string.h>
7
8/* Prints the call stack, that is, a list of addresses, one in
9 each of the functions we are nested within. gdb or addr2line
10 may be applied to kernel.o to translate these into file names,
11 line numbers, and function names. */
12void
13debug_backtrace (void)
14{
15 static bool explained;
16 void **frame;
17
18 printf ("Call stack: %p", __builtin_return_address (0));
19 for (frame = __builtin_frame_address (1);
20 (uintptr_t) frame >= 0x1000 && frame[0] != NULL;
21 frame = frame[0])
22 printf (" %p", frame[1]);
23 printf (".\n");
24
25 if (!explained)
26 {
27 explained = true;
28 printf ("The `backtrace' program can make call stacks useful.\n"
29 "Read \"Backtraces\" in the \"Debugging Tools\" chapter\n"
30 "of the Pintos documentation for more information.\n");
31 }
32}
diff --git a/pintos-progos/lib/debug.h b/pintos-progos/lib/debug.h
new file mode 100644
index 0000000..888ab7b
--- /dev/null
+++ b/pintos-progos/lib/debug.h
@@ -0,0 +1,39 @@
1#ifndef __LIB_DEBUG_H
2#define __LIB_DEBUG_H
3
4/* GCC lets us add "attributes" to functions, function
5 parameters, etc. to indicate their properties.
6 See the GCC manual for details. */
7#define UNUSED __attribute__ ((unused))
8#define NO_RETURN __attribute__ ((noreturn))
9#define NO_INLINE __attribute__ ((noinline))
10#define PRINTF_FORMAT(FMT, FIRST) __attribute__ ((format (printf, FMT, FIRST)))
11
12/* Halts the OS, printing the source file name, line number, and
13 function name, plus a user-specific message. */
14#define PANIC(...) debug_panic (__FILE__, __LINE__, __func__, __VA_ARGS__)
15
16void debug_panic (const char *file, int line, const char *function,
17 const char *message, ...) PRINTF_FORMAT (4, 5) NO_RETURN;
18void debug_backtrace (void);
19void debug_backtrace_all (void);
20
21#endif
22
23
24
25/* This is outside the header guard so that debug.h may be
26 included multiple times with different settings of NDEBUG. */
27#undef ASSERT
28#undef NOT_REACHED
29
30#ifndef NDEBUG
31#define ASSERT(CONDITION) \
32 if (CONDITION) { } else { \
33 PANIC ("assertion `%s' failed.", #CONDITION); \
34 }
35#define NOT_REACHED() PANIC ("executed an unreachable statement");
36#else
37#define ASSERT(CONDITION) ((void) 0)
38#define NOT_REACHED() for (;;)
39#endif /* lib/debug.h */
diff --git a/pintos-progos/lib/inttypes.h b/pintos-progos/lib/inttypes.h
new file mode 100644
index 0000000..f703725
--- /dev/null
+++ b/pintos-progos/lib/inttypes.h
@@ -0,0 +1,48 @@
1#ifndef __LIB_INTTYPES_H
2#define __LIB_INTTYPES_H
3
4#include <stdint.h>
5
6#define PRId8 "hhd"
7#define PRIi8 "hhi"
8#define PRIo8 "hho"
9#define PRIu8 "hhu"
10#define PRIx8 "hhx"
11#define PRIX8 "hhX"
12
13#define PRId16 "hd"
14#define PRIi16 "hi"
15#define PRIo16 "ho"
16#define PRIu16 "hu"
17#define PRIx16 "hx"
18#define PRIX16 "hX"
19
20#define PRId32 "d"
21#define PRIi32 "i"
22#define PRIo32 "o"
23#define PRIu32 "u"
24#define PRIx32 "x"
25#define PRIX32 "X"
26
27#define PRId64 "lld"
28#define PRIi64 "lli"
29#define PRIo64 "llo"
30#define PRIu64 "llu"
31#define PRIx64 "llx"
32#define PRIX64 "llX"
33
34#define PRIdMAX "jd"
35#define PRIiMAX "ji"
36#define PRIoMAX "jo"
37#define PRIuMAX "ju"
38#define PRIxMAX "jx"
39#define PRIXMAX "jX"
40
41#define PRIdPTR "td"
42#define PRIiPTR "ti"
43#define PRIoPTR "to"
44#define PRIuPTR "tu"
45#define PRIxPTR "tx"
46#define PRIXPTR "tX"
47
48#endif /* lib/inttypes.h */
diff --git a/pintos-progos/lib/kernel/bitmap.c b/pintos-progos/lib/kernel/bitmap.c
new file mode 100644
index 0000000..d14a98c
--- /dev/null
+++ b/pintos-progos/lib/kernel/bitmap.c
@@ -0,0 +1,371 @@
1#include "bitmap.h"
2#include <debug.h>
3#include <limits.h>
4#include <round.h>
5#include <stdio.h>
6#include "threads/malloc.h"
7#ifdef FILESYS
8#include "filesys/file.h"
9#endif
10
11/* Element type.
12
13 This must be an unsigned integer type at least as wide as int.
14
15 Each bit represents one bit in the bitmap.
16 If bit 0 in an element represents bit K in the bitmap,
17 then bit 1 in the element represents bit K+1 in the bitmap,
18 and so on. */
19typedef unsigned long elem_type;
20
21/* Number of bits in an element. */
22#define ELEM_BITS (sizeof (elem_type) * CHAR_BIT)
23
24/* From the outside, a bitmap is an array of bits. From the
25 inside, it's an array of elem_type (defined above) that
26 simulates an array of bits. */
27struct bitmap
28 {
29 size_t bit_cnt; /* Number of bits. */
30 elem_type *bits; /* Elements that represent bits. */
31 };
32
33/* Returns the index of the element that contains the bit
34 numbered BIT_IDX. */
35static inline size_t
36elem_idx (size_t bit_idx)
37{
38 return bit_idx / ELEM_BITS;
39}
40
41/* Returns an elem_type where only the bit corresponding to
42 BIT_IDX is turned on. */
43static inline elem_type
44bit_mask (size_t bit_idx)
45{
46 return (elem_type) 1 << (bit_idx % ELEM_BITS);
47}
48
49/* Returns the number of elements required for BIT_CNT bits. */
50static inline size_t
51elem_cnt (size_t bit_cnt)
52{
53 return DIV_ROUND_UP (bit_cnt, ELEM_BITS);
54}
55
56/* Returns the number of bytes required for BIT_CNT bits. */
57static inline size_t
58byte_cnt (size_t bit_cnt)
59{
60 return sizeof (elem_type) * elem_cnt (bit_cnt);
61}
62
63/* Returns a bit mask in which the bits actually used in the last
64 element of B's bits are set to 1 and the rest are set to 0. */
65static inline elem_type
66last_mask (const struct bitmap *b)
67{
68 int last_bits = b->bit_cnt % ELEM_BITS;
69 return last_bits ? ((elem_type) 1 << last_bits) - 1 : (elem_type) -1;
70}
71
72/* Creation and destruction. */
73
74/* Creates and returns a pointer to a newly allocated bitmap with room for
75 BIT_CNT (or more) bits. Returns a null pointer if memory allocation fails.
76 The caller is responsible for freeing the bitmap, with bitmap_destroy(),
77 when it is no longer needed. */
78struct bitmap *
79bitmap_create (size_t bit_cnt)
80{
81 struct bitmap *b = malloc (sizeof *b);
82 if (b != NULL)
83 {
84 b->bit_cnt = bit_cnt;
85 b->bits = malloc (byte_cnt (bit_cnt));
86 if (b->bits != NULL || bit_cnt == 0)
87 {
88 bitmap_set_all (b, false);
89 return b;
90 }
91 free (b);
92 }
93 return NULL;
94}
95
96/* Creates and returns a bitmap with BIT_CNT bits in the
97 BLOCK_SIZE bytes of storage preallocated at BLOCK.
98 BLOCK_SIZE must be at least bitmap_needed_bytes(BIT_CNT). */
99struct bitmap *
100bitmap_create_in_buf (size_t bit_cnt, void *block, size_t block_size UNUSED)
101{
102 struct bitmap *b = block;
103
104 ASSERT (block_size >= bitmap_buf_size (bit_cnt));
105
106 b->bit_cnt = bit_cnt;
107 b->bits = (elem_type *) (b + 1);
108 bitmap_set_all (b, false);
109 return b;
110}
111
112/* Returns the number of bytes required to accomodate a bitmap
113 with BIT_CNT bits (for use with bitmap_create_in_buf()). */
114size_t
115bitmap_buf_size (size_t bit_cnt)
116{
117 return sizeof (struct bitmap) + byte_cnt (bit_cnt);
118}
119
120/* Destroys bitmap B, freeing its storage.
121 Not for use on bitmaps created by bitmap_create_in_buf(). */
122void
123bitmap_destroy (struct bitmap *b)
124{
125 if (b != NULL)
126 {
127 free (b->bits);
128 free (b);
129 }
130}
131
132/* Bitmap size. */
133
134/* Returns the number of bits in B. */
135size_t
136bitmap_size (const struct bitmap *b)
137{
138 return b->bit_cnt;
139}
140
141/* Setting and testing single bits. */
142
143/* Atomically sets the bit numbered IDX in B to VALUE. */
144void
145bitmap_set (struct bitmap *b, size_t idx, bool value)
146{
147 ASSERT (b != NULL);
148 ASSERT (idx < b->bit_cnt);
149 if (value)
150 bitmap_mark (b, idx);
151 else
152 bitmap_reset (b, idx);
153}
154
155/* Atomically sets the bit numbered BIT_IDX in B to true. */
156void
157bitmap_mark (struct bitmap *b, size_t bit_idx)
158{
159 size_t idx = elem_idx (bit_idx);
160 elem_type mask = bit_mask (bit_idx);
161
162 /* This is equivalent to `b->bits[idx] |= mask' except that it
163 is guaranteed to be atomic on a uniprocessor machine. See
164 the description of the OR instruction in [IA32-v2b]. */
165 asm ("orl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc");
166}
167
168/* Atomically sets the bit numbered BIT_IDX in B to false. */
169void
170bitmap_reset (struct bitmap *b, size_t bit_idx)
171{
172 size_t idx = elem_idx (bit_idx);
173 elem_type mask = bit_mask (bit_idx);
174
175 /* This is equivalent to `b->bits[idx] &= ~mask' except that it
176 is guaranteed to be atomic on a uniprocessor machine. See
177 the description of the AND instruction in [IA32-v2a]. */
178 asm ("andl %1, %0" : "=m" (b->bits[idx]) : "r" (~mask) : "cc");
179}
180
181/* Atomically toggles the bit numbered IDX in B;
182 that is, if it is true, makes it false,
183 and if it is false, makes it true. */
184void
185bitmap_flip (struct bitmap *b, size_t bit_idx)
186{
187 size_t idx = elem_idx (bit_idx);
188 elem_type mask = bit_mask (bit_idx);
189
190 /* This is equivalent to `b->bits[idx] ^= mask' except that it
191 is guaranteed to be atomic on a uniprocessor machine. See
192 the description of the XOR instruction in [IA32-v2b]. */
193 asm ("xorl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc");
194}
195
196/* Returns the value of the bit numbered IDX in B. */
197bool
198bitmap_test (const struct bitmap *b, size_t idx)
199{
200 ASSERT (b != NULL);
201 ASSERT (idx < b->bit_cnt);
202 return (b->bits[elem_idx (idx)] & bit_mask (idx)) != 0;
203}
204
205/* Setting and testing multiple bits. */
206
207/* Sets all bits in B to VALUE. */
208void
209bitmap_set_all (struct bitmap *b, bool value)
210{
211 ASSERT (b != NULL);
212
213 bitmap_set_multiple (b, 0, bitmap_size (b), value);
214}
215
216/* Sets the CNT bits starting at START in B to VALUE. */
217void
218bitmap_set_multiple (struct bitmap *b, size_t start, size_t cnt, bool value)
219{
220 size_t i;
221
222 ASSERT (b != NULL);
223 ASSERT (start <= b->bit_cnt);
224 ASSERT (start + cnt <= b->bit_cnt);
225
226 for (i = 0; i < cnt; i++)
227 bitmap_set (b, start + i, value);
228}
229
230/* Returns the number of bits in B between START and START + CNT,
231 exclusive, that are set to VALUE. */
232size_t
233bitmap_count (const struct bitmap *b, size_t start, size_t cnt, bool value)
234{
235 size_t i, value_cnt;
236
237 ASSERT (b != NULL);
238 ASSERT (start <= b->bit_cnt);
239 ASSERT (start + cnt <= b->bit_cnt);
240
241 value_cnt = 0;
242 for (i = 0; i < cnt; i++)
243 if (bitmap_test (b, start + i) == value)
244 value_cnt++;
245 return value_cnt;
246}
247
248/* Returns true if any bits in B between START and START + CNT,
249 exclusive, are set to VALUE, and false otherwise. */
250bool
251bitmap_contains (const struct bitmap *b, size_t start, size_t cnt, bool value)
252{
253 size_t i;
254
255 ASSERT (b != NULL);
256 ASSERT (start <= b->bit_cnt);
257 ASSERT (start + cnt <= b->bit_cnt);
258
259 for (i = 0; i < cnt; i++)
260 if (bitmap_test (b, start + i) == value)
261 return true;
262 return false;
263}
264
265/* Returns true if any bits in B between START and START + CNT,
266 exclusive, are set to true, and false otherwise.*/
267bool
268bitmap_any (const struct bitmap *b, size_t start, size_t cnt)
269{
270 return bitmap_contains (b, start, cnt, true);
271}
272
273/* Returns true if no bits in B between START and START + CNT,
274 exclusive, are set to true, and false otherwise.*/
275bool
276bitmap_none (const struct bitmap *b, size_t start, size_t cnt)
277{
278 return !bitmap_contains (b, start, cnt, true);
279}
280
281/* Returns true if every bit in B between START and START + CNT,
282 exclusive, is set to true, and false otherwise. */
283bool
284bitmap_all (const struct bitmap *b, size_t start, size_t cnt)
285{
286 return !bitmap_contains (b, start, cnt, false);
287}
288
289/* Finding set or unset bits. */
290
291/* Finds and returns the starting index of the first group of CNT
292 consecutive bits in B at or after START that are all set to
293 VALUE.
294 If there is no such group, returns BITMAP_ERROR. */
295size_t
296bitmap_scan (const struct bitmap *b, size_t start, size_t cnt, bool value)
297{
298 ASSERT (b != NULL);
299 ASSERT (start <= b->bit_cnt);
300
301 if (cnt <= b->bit_cnt)
302 {
303 size_t last = b->bit_cnt - cnt;
304 size_t i;
305 for (i = start; i <= last; i++)
306 if (!bitmap_contains (b, i, cnt, !value))
307 return i;
308 }
309 return BITMAP_ERROR;
310}
311
312/* Finds the first group of CNT consecutive bits in B at or after
313 START that are all set to VALUE, flips them all to !VALUE,
314 and returns the index of the first bit in the group.
315 If there is no such group, returns BITMAP_ERROR.
316 If CNT is zero, returns 0.
317 Bits are set atomically, but testing bits is not atomic with
318 setting them. */
319size_t
320bitmap_scan_and_flip (struct bitmap *b, size_t start, size_t cnt, bool value)
321{
322 size_t idx = bitmap_scan (b, start, cnt, value);
323 if (idx != BITMAP_ERROR)
324 bitmap_set_multiple (b, idx, cnt, !value);
325 return idx;
326}
327
328/* File input and output. */
329
330#ifdef FILESYS
331/* Returns the number of bytes needed to store B in a file. */
332size_t
333bitmap_file_size (const struct bitmap *b)
334{
335 return byte_cnt (b->bit_cnt);
336}
337
338/* Reads B from FILE. Returns true if successful, false
339 otherwise. */
340bool
341bitmap_read (struct bitmap *b, struct file *file)
342{
343 bool success = true;
344 if (b->bit_cnt > 0)
345 {
346 off_t size = byte_cnt (b->bit_cnt);
347 success = file_read_at (file, b->bits, size, 0) == size;
348 b->bits[elem_cnt (b->bit_cnt) - 1] &= last_mask (b);
349 }
350 return success;
351}
352
353/* Writes B to FILE. Return true if successful, false
354 otherwise. */
355bool
356bitmap_write (const struct bitmap *b, struct file *file)
357{
358 off_t size = byte_cnt (b->bit_cnt);
359 return file_write_at (file, b->bits, size, 0) == size;
360}
361#endif /* FILESYS */
362
363/* Debugging. */
364
365/* Dumps the contents of B to the console as hexadecimal. */
366void
367bitmap_dump (const struct bitmap *b)
368{
369 hex_dump (0, b->bits, byte_cnt (b->bit_cnt), false);
370}
371
diff --git a/pintos-progos/lib/kernel/bitmap.h b/pintos-progos/lib/kernel/bitmap.h
new file mode 100644
index 0000000..a50593c
--- /dev/null
+++ b/pintos-progos/lib/kernel/bitmap.h
@@ -0,0 +1,51 @@
1#ifndef __LIB_KERNEL_BITMAP_H
2#define __LIB_KERNEL_BITMAP_H
3
4#include <stdbool.h>
5#include <stddef.h>
6#include <inttypes.h>
7
8/* Bitmap abstract data type. */
9
10/* Creation and destruction. */
11struct bitmap *bitmap_create (size_t bit_cnt);
12struct bitmap *bitmap_create_in_buf (size_t bit_cnt, void *, size_t byte_cnt);
13size_t bitmap_buf_size (size_t bit_cnt);
14void bitmap_destroy (struct bitmap *);
15
16/* Bitmap size. */
17size_t bitmap_size (const struct bitmap *);
18
19/* Setting and testing single bits. */
20void bitmap_set (struct bitmap *, size_t idx, bool);
21void bitmap_mark (struct bitmap *, size_t idx);
22void bitmap_reset (struct bitmap *, size_t idx);
23void bitmap_flip (struct bitmap *, size_t idx);
24bool bitmap_test (const struct bitmap *, size_t idx);
25
26/* Setting and testing multiple bits. */
27void bitmap_set_all (struct bitmap *, bool);
28void bitmap_set_multiple (struct bitmap *, size_t start, size_t cnt, bool);
29size_t bitmap_count (const struct bitmap *, size_t start, size_t cnt, bool);
30bool bitmap_contains (const struct bitmap *, size_t start, size_t cnt, bool);
31bool bitmap_any (const struct bitmap *, size_t start, size_t cnt);
32bool bitmap_none (const struct bitmap *, size_t start, size_t cnt);
33bool bitmap_all (const struct bitmap *, size_t start, size_t cnt);
34
35/* Finding set or unset bits. */
36#define BITMAP_ERROR SIZE_MAX
37size_t bitmap_scan (const struct bitmap *, size_t start, size_t cnt, bool);
38size_t bitmap_scan_and_flip (struct bitmap *, size_t start, size_t cnt, bool);
39
40/* File input and output. */
41#ifdef FILESYS
42struct file;
43size_t bitmap_file_size (const struct bitmap *);
44bool bitmap_read (struct bitmap *, struct file *);
45bool bitmap_write (const struct bitmap *, struct file *);
46#endif
47
48/* Debugging. */
49void bitmap_dump (const struct bitmap *);
50
51#endif /* lib/kernel/bitmap.h */
diff --git a/pintos-progos/lib/kernel/console.c b/pintos-progos/lib/kernel/console.c
new file mode 100644
index 0000000..844b184
--- /dev/null
+++ b/pintos-progos/lib/kernel/console.c
@@ -0,0 +1,191 @@
1#include <console.h>
2#include <stdarg.h>
3#include <stdio.h>
4#include "devices/serial.h"
5#include "devices/vga.h"
6#include "threads/init.h"
7#include "threads/interrupt.h"
8#include "threads/synch.h"
9
10static void vprintf_helper (char, void *);
11static void putchar_have_lock (uint8_t c);
12
13/* The console lock.
14 Both the vga and serial layers do their own locking, so it's
15 safe to call them at any time.
16 But this lock is useful to prevent simultaneous printf() calls
17 from mixing their output, which looks confusing. */
18static struct lock console_lock;
19
20/* True in ordinary circumstances: we want to use the console
21 lock to avoid mixing output between threads, as explained
22 above.
23
24 False in early boot before the point that locks are functional
25 or the console lock has been initialized, or after a kernel
26 panics. In the former case, taking the lock would cause an
27 assertion failure, which in turn would cause a panic, turning
28 it into the latter case. In the latter case, if it is a buggy
29 lock_acquire() implementation that caused the panic, we'll
30 likely just recurse. */
31static bool use_console_lock;
32
33/* It's possible, if you add enough debug output to Pintos, to
34 try to recursively grab console_lock from a single thread. As
35 a real example, I added a printf() call to palloc_free().
36 Here's a real backtrace that resulted:
37
38 lock_console()
39 vprintf()
40 printf() - palloc() tries to grab the lock again
41 palloc_free()
42 thread_schedule_tail() - another thread dying as we switch threads
43 schedule()
44 thread_yield()
45 intr_handler() - timer interrupt
46 intr_set_level()
47 serial_putc()
48 putchar_have_lock()
49 putbuf()
50 sys_write() - one process writing to the console
51 syscall_handler()
52 intr_handler()
53
54 This kind of thing is very difficult to debug, so we avoid the
55 problem by simulating a recursive lock with a depth
56 counter. */
57static int console_lock_depth;
58
59/* Number of characters written to console. */
60static int64_t write_cnt;
61
62/* Enable console locking. */
63void
64console_init (void)
65{
66 lock_init (&console_lock);
67 use_console_lock = true;
68}
69
70/* Notifies the console that a kernel panic is underway,
71 which warns it to avoid trying to take the console lock from
72 now on. */
73void
74console_panic (void)
75{
76 use_console_lock = false;
77}
78
79/* Prints console statistics. */
80void
81console_print_stats (void)
82{
83 printf ("Console: %lld characters output\n", write_cnt);
84}
85
86/* Acquires the console lock. */
87static void
88acquire_console (void)
89{
90 if (!intr_context () && use_console_lock)
91 {
92 if (lock_held_by_current_thread (&console_lock))
93 console_lock_depth++;
94 else
95 lock_acquire (&console_lock);
96 }
97}
98
99/* Releases the console lock. */
100static void
101release_console (void)
102{
103 if (!intr_context () && use_console_lock)
104 {
105 if (console_lock_depth > 0)
106 console_lock_depth--;
107 else
108 lock_release (&console_lock);
109 }
110}
111
112/* Returns true if the current thread has the console lock,
113 false otherwise. */
114static bool
115console_locked_by_current_thread (void)
116{
117 return (intr_context ()
118 || !use_console_lock
119 || lock_held_by_current_thread (&console_lock));
120}
121
122/* The standard vprintf() function,
123 which is like printf() but uses a va_list.
124 Writes its output to both vga display and serial port. */
125int
126vprintf (const char *format, va_list args)
127{
128 int char_cnt = 0;
129
130 acquire_console ();
131 __vprintf (format, args, vprintf_helper, &char_cnt);
132 release_console ();
133
134 return char_cnt;
135}
136
137/* Writes string S to the console, followed by a new-line
138 character. */
139int
140puts (const char *s)
141{
142 acquire_console ();
143 while (*s != '\0')
144 putchar_have_lock (*s++);
145 putchar_have_lock ('\n');
146 release_console ();
147
148 return 0;
149}
150
151/* Writes the N characters in BUFFER to the console. */
152void
153putbuf (const char *buffer, size_t n)
154{
155 acquire_console ();
156 while (n-- > 0)
157 putchar_have_lock (*buffer++);
158 release_console ();
159}
160
161/* Writes C to the vga display and serial port. */
162int
163putchar (int c)
164{
165 acquire_console ();
166 putchar_have_lock (c);
167 release_console ();
168
169 return c;
170}
171
172/* Helper function for vprintf(). */
173static void
174vprintf_helper (char c, void *char_cnt_)
175{
176 int *char_cnt = char_cnt_;
177 (*char_cnt)++;
178 putchar_have_lock (c);
179}
180
181/* Writes C to the vga display and serial port.
182 The caller has already acquired the console lock if
183 appropriate. */
184static void
185putchar_have_lock (uint8_t c)
186{
187 ASSERT (console_locked_by_current_thread ());
188 write_cnt++;
189 serial_putc (c);
190 vga_putc (c);
191}
diff --git a/pintos-progos/lib/kernel/console.h b/pintos-progos/lib/kernel/console.h
new file mode 100644
index 0000000..ab99249
--- /dev/null
+++ b/pintos-progos/lib/kernel/console.h
@@ -0,0 +1,8 @@
1#ifndef __LIB_KERNEL_CONSOLE_H
2#define __LIB_KERNEL_CONSOLE_H
3
4void console_init (void);
5void console_panic (void);
6void console_print_stats (void);
7
8#endif /* lib/kernel/console.h */
diff --git a/pintos-progos/lib/kernel/debug.c b/pintos-progos/lib/kernel/debug.c
new file mode 100644
index 0000000..b12f4f9
--- /dev/null
+++ b/pintos-progos/lib/kernel/debug.c
@@ -0,0 +1,123 @@
1#include <debug.h>
2#include <console.h>
3#include <stdarg.h>
4#include <stdbool.h>
5#include <stddef.h>
6#include <stdio.h>
7#include <string.h>
8#include "threads/init.h"
9#include "threads/interrupt.h"
10#include "threads/thread.h"
11#include "threads/switch.h"
12#include "threads/vaddr.h"
13#include "devices/serial.h"
14#include "devices/shutdown.h"
15
16/* Halts the OS, printing the source file name, line number, and
17 function name, plus a user-specific message. */
18void
19debug_panic (const char *file, int line, const char *function,
20 const char *message, ...)
21{
22 static int level;
23 va_list args;
24
25 intr_disable ();
26 console_panic ();
27
28 level++;
29 if (level == 1)
30 {
31 printf ("Kernel PANIC at %s:%d in %s(): ", file, line, function);
32
33 va_start (args, message);
34 vprintf (message, args);
35 printf ("\n");
36 va_end (args);
37
38 debug_backtrace ();
39 }
40 else if (level == 2)
41 printf ("Kernel PANIC recursion at %s:%d in %s().\n",
42 file, line, function);
43 else
44 {
45 /* Don't print anything: that's probably why we recursed. */
46 }
47
48 serial_flush ();
49 shutdown ();
50 for (;;);
51}
52
53/* Print call stack of a thread.
54 The thread may be running, ready, or blocked. */
55static void
56print_stacktrace(struct thread *t, void *aux UNUSED)
57{
58 void *retaddr = NULL, **frame = NULL;
59 const char *status = "UNKNOWN";
60
61 switch (t->status) {
62 case THREAD_RUNNING:
63 status = "RUNNING";
64 break;
65
66 case THREAD_READY:
67 status = "READY";
68 break;
69
70 case THREAD_BLOCKED:
71 status = "BLOCKED";
72 break;
73
74 default:
75 break;
76 }
77
78 printf ("Call stack of thread `%s' (status %s):", t->name, status);
79
80 if (t == thread_current())
81 {
82 frame = __builtin_frame_address (1);
83 retaddr = __builtin_return_address (0);
84 }
85 else
86 {
87 /* Retrieve the values of the base and instruction pointers
88 as they were saved when this thread called switch_threads. */
89 struct switch_threads_frame * saved_frame;
90
91 saved_frame = (struct switch_threads_frame *)t->stack;
92
93 /* Skip threads if they have been added to the all threads
94 list, but have never been scheduled.
95 We can identify because their `stack' member either points
96 at the top of their kernel stack page, or the
97 switch_threads_frame's 'eip' member points at switch_entry.
98 See also threads.c. */
99 if (t->stack == (uint8_t *)t + PGSIZE || saved_frame->eip == switch_entry)
100 {
101 printf (" thread was never scheduled.\n");
102 return;
103 }
104
105 frame = (void **) saved_frame->ebp;
106 retaddr = (void *) saved_frame->eip;
107 }
108
109 printf (" %p", retaddr);
110 for (; (uintptr_t) frame >= 0x1000 && frame[0] != NULL; frame = frame[0])
111 printf (" %p", frame[1]);
112 printf (".\n");
113}
114
115/* Prints call stack of all threads. */
116void
117debug_backtrace_all (void)
118{
119 enum intr_level oldlevel = intr_disable ();
120
121 thread_foreach (print_stacktrace, 0);
122 intr_set_level (oldlevel);
123}
diff --git a/pintos-progos/lib/kernel/hash.c b/pintos-progos/lib/kernel/hash.c
new file mode 100644
index 0000000..57eed45
--- /dev/null
+++ b/pintos-progos/lib/kernel/hash.c
@@ -0,0 +1,430 @@
1/* Hash table.
2
3 This data structure is thoroughly documented in the Tour of
4 Pintos for Project 3.
5
6 See hash.h for basic information. */
7
8#include "hash.h"
9#include "../debug.h"
10#include "threads/malloc.h"
11
12#define list_elem_to_hash_elem(LIST_ELEM) \
13 list_entry(LIST_ELEM, struct hash_elem, list_elem)
14
15static struct list *find_bucket (struct hash *, struct hash_elem *);
16static struct hash_elem *find_elem (struct hash *, struct list *,
17 struct hash_elem *);
18static void insert_elem (struct hash *, struct list *, struct hash_elem *);
19static void remove_elem (struct hash *, struct hash_elem *);
20static void rehash (struct hash *);
21
22/* Initializes hash table H to compute hash values using HASH and
23 compare hash elements using LESS, given auxiliary data AUX. */
24bool
25hash_init (struct hash *h,
26 hash_hash_func *hash, hash_less_func *less, void *aux)
27{
28 h->elem_cnt = 0;
29 h->bucket_cnt = 4;
30 h->buckets = malloc (sizeof *h->buckets * h->bucket_cnt);
31 h->hash = hash;
32 h->less = less;
33 h->aux = aux;
34
35 if (h->buckets != NULL)
36 {
37 hash_clear (h, NULL);
38 return true;
39 }
40 else
41 return false;
42}
43
44/* Removes all the elements from H.
45
46 If DESTRUCTOR is non-null, then it is called for each element
47 in the hash. DESTRUCTOR may, if appropriate, deallocate the
48 memory used by the hash element. However, modifying hash
49 table H while hash_clear() is running, using any of the
50 functions hash_clear(), hash_destroy(), hash_insert(),
51 hash_replace(), or hash_delete(), yields undefined behavior,
52 whether done in DESTRUCTOR or elsewhere. */
53void
54hash_clear (struct hash *h, hash_action_func *destructor)
55{
56 size_t i;
57
58 for (i = 0; i < h->bucket_cnt; i++)
59 {
60 struct list *bucket = &h->buckets[i];
61
62 if (destructor != NULL)
63 while (!list_empty (bucket))
64 {
65 struct list_elem *list_elem = list_pop_front (bucket);
66 struct hash_elem *hash_elem = list_elem_to_hash_elem (list_elem);
67 destructor (hash_elem, h->aux);
68 }
69
70 list_init (bucket);
71 }
72
73 h->elem_cnt = 0;
74}
75
76/* Destroys hash table H.
77
78 If DESTRUCTOR is non-null, then it is first called for each
79 element in the hash. DESTRUCTOR may, if appropriate,
80 deallocate the memory used by the hash element. However,
81 modifying hash table H while hash_clear() is running, using
82 any of the functions hash_clear(), hash_destroy(),
83 hash_insert(), hash_replace(), or hash_delete(), yields
84 undefined behavior, whether done in DESTRUCTOR or
85 elsewhere. */
86void
87hash_destroy (struct hash *h, hash_action_func *destructor)
88{
89 if (destructor != NULL)
90 hash_clear (h, destructor);
91 free (h->buckets);
92}
93
94/* Inserts NEW into hash table H and returns a null pointer, if
95 no equal element is already in the table.
96 If an equal element is already in the table, returns it
97 without inserting NEW. */
98struct hash_elem *
99hash_insert (struct hash *h, struct hash_elem *new)
100{
101 struct list *bucket = find_bucket (h, new);
102 struct hash_elem *old = find_elem (h, bucket, new);
103
104 if (old == NULL)
105 insert_elem (h, bucket, new);
106
107 rehash (h);
108
109 return old;
110}
111
112/* Inserts NEW into hash table H, replacing any equal element
113 already in the table, which is returned. */
114struct hash_elem *
115hash_replace (struct hash *h, struct hash_elem *new)
116{
117 struct list *bucket = find_bucket (h, new);
118 struct hash_elem *old = find_elem (h, bucket, new);
119
120 if (old != NULL)
121 remove_elem (h, old);
122 insert_elem (h, bucket, new);
123
124 rehash (h);
125
126 return old;
127}
128
129/* Finds and returns an element equal to E in hash table H, or a
130 null pointer if no equal element exists in the table. */
131struct hash_elem *
132hash_find (struct hash *h, struct hash_elem *e)
133{
134 return find_elem (h, find_bucket (h, e), e);
135}
136
137/* Finds, removes, and returns an element equal to E in hash
138 table H. Returns a null pointer if no equal element existed
139 in the table.
140
141 If the elements of the hash table are dynamically allocated,
142 or own resources that are, then it is the caller's
143 responsibility to deallocate them. */
144struct hash_elem *
145hash_delete (struct hash *h, struct hash_elem *e)
146{
147 struct hash_elem *found = find_elem (h, find_bucket (h, e), e);
148 if (found != NULL)
149 {
150 remove_elem (h, found);
151 rehash (h);
152 }
153 return found;
154}
155
156/* Calls ACTION for each element in hash table H in arbitrary
157 order.
158 Modifying hash table H while hash_apply() is running, using
159 any of the functions hash_clear(), hash_destroy(),
160 hash_insert(), hash_replace(), or hash_delete(), yields
161 undefined behavior, whether done from ACTION or elsewhere. */
162void
163hash_apply (struct hash *h, hash_action_func *action)
164{
165 size_t i;
166
167 ASSERT (action != NULL);
168
169 for (i = 0; i < h->bucket_cnt; i++)
170 {
171 struct list *bucket = &h->buckets[i];
172 struct list_elem *elem, *next;
173
174 for (elem = list_begin (bucket); elem != list_end (bucket); elem = next)
175 {
176 next = list_next (elem);
177 action (list_elem_to_hash_elem (elem), h->aux);
178 }
179 }
180}
181
182/* Initializes I for iterating hash table H.
183
184 Iteration idiom:
185
186 struct hash_iterator i;
187
188 hash_first (&i, h);
189 while (hash_next (&i))
190 {
191 struct foo *f = hash_entry (hash_cur (&i), struct foo, elem);
192 ...do something with f...
193 }
194
195 Modifying hash table H during iteration, using any of the
196 functions hash_clear(), hash_destroy(), hash_insert(),
197 hash_replace(), or hash_delete(), invalidates all
198 iterators. */
199void
200hash_first (struct hash_iterator *i, struct hash *h)
201{
202 ASSERT (i != NULL);
203 ASSERT (h != NULL);
204
205 i->hash = h;
206 i->bucket = i->hash->buckets;
207 i->elem = list_elem_to_hash_elem (list_head (i->bucket));
208}
209
210/* Advances I to the next element in the hash table and returns
211 it. Returns a null pointer if no elements are left. Elements
212 are returned in arbitrary order.
213
214 Modifying a hash table H during iteration, using any of the
215 functions hash_clear(), hash_destroy(), hash_insert(),
216 hash_replace(), or hash_delete(), invalidates all
217 iterators. */
218struct hash_elem *
219hash_next (struct hash_iterator *i)
220{
221 ASSERT (i != NULL);
222
223 i->elem = list_elem_to_hash_elem (list_next (&i->elem->list_elem));
224 while (i->elem == list_elem_to_hash_elem (list_end (i->bucket)))
225 {
226 if (++i->bucket >= i->hash->buckets + i->hash->bucket_cnt)
227 {
228 i->elem = NULL;
229 break;
230 }
231 i->elem = list_elem_to_hash_elem (list_begin (i->bucket));
232 }
233
234 return i->elem;
235}
236
237/* Returns the current element in the hash table iteration, or a
238 null pointer at the end of the table. Undefined behavior
239 after calling hash_first() but before hash_next(). */
240struct hash_elem *
241hash_cur (struct hash_iterator *i)
242{
243 return i->elem;
244}
245
246/* Returns the number of elements in H. */
247size_t
248hash_size (struct hash *h)
249{
250 return h->elem_cnt;
251}
252
253/* Returns true if H contains no elements, false otherwise. */
254bool
255hash_empty (struct hash *h)
256{
257 return h->elem_cnt == 0;
258}
259
260/* Fowler-Noll-Vo hash constants, for 32-bit word sizes. */
261#define FNV_32_PRIME 16777619u
262#define FNV_32_BASIS 2166136261u
263
264/* Returns a hash of the SIZE bytes in BUF. */
265unsigned
266hash_bytes (const void *buf_, size_t size)
267{
268 /* Fowler-Noll-Vo 32-bit hash, for bytes. */
269 const unsigned char *buf = buf_;
270 unsigned hash;
271
272 ASSERT (buf != NULL);
273
274 hash = FNV_32_BASIS;
275 while (size-- > 0)
276 hash = (hash * FNV_32_PRIME) ^ *buf++;
277
278 return hash;
279}
280
281/* Returns a hash of string S. */
282unsigned
283hash_string (const char *s_)
284{
285 const unsigned char *s = (const unsigned char *) s_;
286 unsigned hash;
287
288 ASSERT (s != NULL);
289
290 hash = FNV_32_BASIS;
291 while (*s != '\0')
292 hash = (hash * FNV_32_PRIME) ^ *s++;
293
294 return hash;
295}
296
297/* Returns a hash of integer I. */
298unsigned
299hash_int (int i)
300{
301 return hash_bytes (&i, sizeof i);
302}
303
304/* Returns the bucket in H that E belongs in. */
305static struct list *
306find_bucket (struct hash *h, struct hash_elem *e)
307{
308 size_t bucket_idx = h->hash (e, h->aux) & (h->bucket_cnt - 1);
309 return &h->buckets[bucket_idx];
310}
311
312/* Searches BUCKET in H for a hash element equal to E. Returns
313 it if found or a null pointer otherwise. */
314static struct hash_elem *
315find_elem (struct hash *h, struct list *bucket, struct hash_elem *e)
316{
317 struct list_elem *i;
318
319 for (i = list_begin (bucket); i != list_end (bucket); i = list_next (i))
320 {
321 struct hash_elem *hi = list_elem_to_hash_elem (i);
322 if (!h->less (hi, e, h->aux) && !h->less (e, hi, h->aux))
323 return hi;
324 }
325 return NULL;
326}
327
328/* Returns X with its lowest-order bit set to 1 turned off. */
329static inline size_t
330turn_off_least_1bit (size_t x)
331{
332 return x & (x - 1);
333}
334
335/* Returns true if X is a power of 2, otherwise false. */
336static inline size_t
337is_power_of_2 (size_t x)
338{
339 return x != 0 && turn_off_least_1bit (x) == 0;
340}
341
342/* Element per bucket ratios. */
343#define MIN_ELEMS_PER_BUCKET 1 /* Elems/bucket < 1: reduce # of buckets. */
344#define BEST_ELEMS_PER_BUCKET 2 /* Ideal elems/bucket. */
345#define MAX_ELEMS_PER_BUCKET 4 /* Elems/bucket > 4: increase # of buckets. */
346
347/* Changes the number of buckets in hash table H to match the
348 ideal. This function can fail because of an out-of-memory
349 condition, but that'll just make hash accesses less efficient;
350 we can still continue. */
351static void
352rehash (struct hash *h)
353{
354 size_t old_bucket_cnt, new_bucket_cnt;
355 struct list *new_buckets, *old_buckets;
356 size_t i;
357
358 ASSERT (h != NULL);
359
360 /* Save old bucket info for later use. */
361 old_buckets = h->buckets;
362 old_bucket_cnt = h->bucket_cnt;
363
364 /* Calculate the number of buckets to use now.
365 We want one bucket for about every BEST_ELEMS_PER_BUCKET.
366 We must have at least four buckets, and the number of
367 buckets must be a power of 2. */
368 new_bucket_cnt = h->elem_cnt / BEST_ELEMS_PER_BUCKET;
369 if (new_bucket_cnt < 4)
370 new_bucket_cnt = 4;
371 while (!is_power_of_2 (new_bucket_cnt))
372 new_bucket_cnt = turn_off_least_1bit (new_bucket_cnt);
373
374 /* Don't do anything if the bucket count wouldn't change. */
375 if (new_bucket_cnt == old_bucket_cnt)
376 return;
377
378 /* Allocate new buckets and initialize them as empty. */
379 new_buckets = malloc (sizeof *new_buckets * new_bucket_cnt);
380 if (new_buckets == NULL)
381 {
382 /* Allocation failed. This means that use of the hash table will
383 be less efficient. However, it is still usable, so
384 there's no reason for it to be an error. */
385 return;
386 }
387 for (i = 0; i < new_bucket_cnt; i++)
388 list_init (&new_buckets[i]);
389
390 /* Install new bucket info. */
391 h->buckets = new_buckets;
392 h->bucket_cnt = new_bucket_cnt;
393
394 /* Move each old element into the appropriate new bucket. */
395 for (i = 0; i < old_bucket_cnt; i++)
396 {
397 struct list *old_bucket;
398 struct list_elem *elem, *next;
399
400 old_bucket = &old_buckets[i];
401 for (elem = list_begin (old_bucket);
402 elem != list_end (old_bucket); elem = next)
403 {
404 struct list *new_bucket
405 = find_bucket (h, list_elem_to_hash_elem (elem));
406 next = list_next (elem);
407 list_remove (elem);
408 list_push_front (new_bucket, elem);
409 }
410 }
411
412 free (old_buckets);
413}
414
415/* Inserts E into BUCKET (in hash table H). */
416static void
417insert_elem (struct hash *h, struct list *bucket, struct hash_elem *e)
418{
419 h->elem_cnt++;
420 list_push_front (bucket, &e->list_elem);
421}
422
423/* Removes E from hash table H. */
424static void
425remove_elem (struct hash *h, struct hash_elem *e)
426{
427 h->elem_cnt--;
428 list_remove (&e->list_elem);
429}
430
diff --git a/pintos-progos/lib/kernel/hash.h b/pintos-progos/lib/kernel/hash.h
new file mode 100644
index 0000000..db9f674
--- /dev/null
+++ b/pintos-progos/lib/kernel/hash.h
@@ -0,0 +1,103 @@
1#ifndef __LIB_KERNEL_HASH_H
2#define __LIB_KERNEL_HASH_H
3
4/* Hash table.
5
6 This data structure is thoroughly documented in the Tour of
7 Pintos for Project 3.
8
9 This is a standard hash table with chaining. To locate an
10 element in the table, we compute a hash function over the
11 element's data and use that as an index into an array of
12 doubly linked lists, then linearly search the list.
13
14 The chain lists do not use dynamic allocation. Instead, each
15 structure that can potentially be in a hash must embed a
16 struct hash_elem member. All of the hash functions operate on
17 these `struct hash_elem's. The hash_entry macro allows
18 conversion from a struct hash_elem back to a structure object
19 that contains it. This is the same technique used in the
20 linked list implementation. Refer to lib/kernel/list.h for a
21 detailed explanation. */
22
23#include <stdbool.h>
24#include <stddef.h>
25#include <stdint.h>
26#include "list.h"
27
28/* Hash element. */
29struct hash_elem
30 {
31 struct list_elem list_elem;
32 };
33
34/* Converts pointer to hash element HASH_ELEM into a pointer to
35 the structure that HASH_ELEM is embedded inside. Supply the
36 name of the outer structure STRUCT and the member name MEMBER
37 of the hash element. See the big comment at the top of the
38 file for an example. */
39#define hash_entry(HASH_ELEM, STRUCT, MEMBER) \
40 ((STRUCT *) ((uint8_t *) &(HASH_ELEM)->list_elem \
41 - offsetof (STRUCT, MEMBER.list_elem)))
42
43/* Computes and returns the hash value for hash element E, given
44 auxiliary data AUX. */
45typedef unsigned hash_hash_func (const struct hash_elem *e, void *aux);
46
47/* Compares the value of two hash elements A and B, given
48 auxiliary data AUX. Returns true if A is less than B, or
49 false if A is greater than or equal to B. */
50typedef bool hash_less_func (const struct hash_elem *a,
51 const struct hash_elem *b,
52 void *aux);
53
54/* Performs some operation on hash element E, given auxiliary
55 data AUX. */
56typedef void hash_action_func (struct hash_elem *e, void *aux);
57
58/* Hash table. */
59struct hash
60 {
61 size_t elem_cnt; /* Number of elements in table. */
62 size_t bucket_cnt; /* Number of buckets, a power of 2. */
63 struct list *buckets; /* Array of `bucket_cnt' lists. */
64 hash_hash_func *hash; /* Hash function. */
65 hash_less_func *less; /* Comparison function. */
66 void *aux; /* Auxiliary data for `hash' and `less'. */
67 };
68
69/* A hash table iterator. */
70struct hash_iterator
71 {
72 struct hash *hash; /* The hash table. */
73 struct list *bucket; /* Current bucket. */
74 struct hash_elem *elem; /* Current hash element in current bucket. */
75 };
76
77/* Basic life cycle. */
78bool hash_init (struct hash *, hash_hash_func *, hash_less_func *, void *aux);
79void hash_clear (struct hash *, hash_action_func *);
80void hash_destroy (struct hash *, hash_action_func *);
81
82/* Search, insertion, deletion. */
83struct hash_elem *hash_insert (struct hash *, struct hash_elem *);
84struct hash_elem *hash_replace (struct hash *, struct hash_elem *);
85struct hash_elem *hash_find (struct hash *, struct hash_elem *);
86struct hash_elem *hash_delete (struct hash *, struct hash_elem *);
87
88/* Iteration. */
89void hash_apply (struct hash *, hash_action_func *);
90void hash_first (struct hash_iterator *, struct hash *);
91struct hash_elem *hash_next (struct hash_iterator *);
92struct hash_elem *hash_cur (struct hash_iterator *);
93
94/* Information. */
95size_t hash_size (struct hash *);
96bool hash_empty (struct hash *);
97
98/* Sample hash functions. */
99unsigned hash_bytes (const void *, size_t);
100unsigned hash_string (const char *);
101unsigned hash_int (int);
102
103#endif /* lib/kernel/hash.h */
diff --git a/pintos-progos/lib/kernel/list.c b/pintos-progos/lib/kernel/list.c
new file mode 100644
index 0000000..316d9ef
--- /dev/null
+++ b/pintos-progos/lib/kernel/list.c
@@ -0,0 +1,524 @@
1#include "list.h"
2#include "../debug.h"
3
4/* Our doubly linked lists have two header elements: the "head"
5 just before the first element and the "tail" just after the
6 last element. The `prev' link of the front header is null, as
7 is the `next' link of the back header. Their other two links
8 point toward each other via the interior elements of the list.
9
10 An empty list looks like this:
11
12 +------+ +------+
13 <---| head |<--->| tail |--->
14 +------+ +------+
15
16 A list with two elements in it looks like this:
17
18 +------+ +-------+ +-------+ +------+
19 <---| head |<--->| 1 |<--->| 2 |<--->| tail |<--->
20 +------+ +-------+ +-------+ +------+
21
22 The symmetry of this arrangement eliminates lots of special
23 cases in list processing. For example, take a look at
24 list_remove(): it takes only two pointer assignments and no
25 conditionals. That's a lot simpler than the code would be
26 without header elements.
27
28 (Because only one of the pointers in each header element is used,
29 we could in fact combine them into a single header element
30 without sacrificing this simplicity. But using two separate
31 elements allows us to do a little bit of checking on some
32 operations, which can be valuable.) */
33
34static bool is_sorted (struct list_elem *a, struct list_elem *b,
35 list_less_func *less, void *aux) UNUSED;
36
37/* Returns true if ELEM is a head, false otherwise. */
38static inline bool
39is_head (struct list_elem *elem)
40{
41 return elem != NULL && elem->prev == NULL && elem->next != NULL;
42}
43
44/* Returns true if ELEM is an interior element,
45 false otherwise. */
46static inline bool
47is_interior (struct list_elem *elem)
48{
49 return elem != NULL && elem->prev != NULL && elem->next != NULL;
50}
51
52/* Returns true if ELEM is a tail, false otherwise. */
53static inline bool
54is_tail (struct list_elem *elem)
55{
56 return elem != NULL && elem->prev != NULL && elem->next == NULL;
57}
58
59/* Initializes LIST as an empty list. */
60void
61list_init (struct list *list)
62{
63 ASSERT (list != NULL);
64 list->head.prev = NULL;
65 list->head.next = &list->tail;
66 list->tail.prev = &list->head;
67 list->tail.next = NULL;
68}
69
70/* Returns the beginning of LIST. */
71struct list_elem *
72list_begin (struct list *list)
73{
74 ASSERT (list != NULL);
75 return list->head.next;
76}
77
78/* Returns the element after ELEM in its list. If ELEM is the
79 last element in its list, returns the list tail. Results are
80 undefined if ELEM is itself a list tail. */
81struct list_elem *
82list_next (struct list_elem *elem)
83{
84 ASSERT (is_head (elem) || is_interior (elem));
85 return elem->next;
86}
87
88/* Returns LIST's tail.
89
90 list_end() is often used in iterating through a list from
91 front to back. See the big comment at the top of list.h for
92 an example. */
93struct list_elem *
94list_end (struct list *list)
95{
96 ASSERT (list != NULL);
97 return &list->tail;
98}
99
100/* Returns the LIST's reverse beginning, for iterating through
101 LIST in reverse order, from back to front. */
102struct list_elem *
103list_rbegin (struct list *list)
104{
105 ASSERT (list != NULL);
106 return list->tail.prev;
107}
108
109/* Returns the element before ELEM in its list. If ELEM is the
110 first element in its list, returns the list head. Results are
111 undefined if ELEM is itself a list head. */
112struct list_elem *
113list_prev (struct list_elem *elem)
114{
115 ASSERT (is_interior (elem) || is_tail (elem));
116 return elem->prev;
117}
118
119/* Returns LIST's head.
120
121 list_rend() is often used in iterating through a list in
122 reverse order, from back to front. Here's typical usage,
123 following the example from the top of list.h:
124
125 for (e = list_rbegin (&foo_list); e != list_rend (&foo_list);
126 e = list_prev (e))
127 {
128 struct foo *f = list_entry (e, struct foo, elem);
129 ...do something with f...
130 }
131*/
132struct list_elem *
133list_rend (struct list *list)
134{
135 ASSERT (list != NULL);
136 return &list->head;
137}
138
139/* Return's LIST's head.
140
141 list_head() can be used for an alternate style of iterating
142 through a list, e.g.:
143
144 e = list_head (&list);
145 while ((e = list_next (e)) != list_end (&list))
146 {
147 ...
148 }
149*/
150struct list_elem *
151list_head (struct list *list)
152{
153 ASSERT (list != NULL);
154 return &list->head;
155}
156
157/* Return's LIST's tail. */
158struct list_elem *
159list_tail (struct list *list)
160{
161 ASSERT (list != NULL);
162 return &list->tail;
163}
164
165/* Inserts ELEM just before BEFORE, which may be either an
166 interior element or a tail. The latter case is equivalent to
167 list_push_back(). */
168void
169list_insert (struct list_elem *before, struct list_elem *elem)
170{
171 ASSERT (is_interior (before) || is_tail (before));
172 ASSERT (elem != NULL);
173
174 elem->prev = before->prev;
175 elem->next = before;
176 before->prev->next = elem;
177 before->prev = elem;
178}
179
180/* Removes elements FIRST though LAST (exclusive) from their
181 current list, then inserts them just before BEFORE, which may
182 be either an interior element or a tail. */
183void
184list_splice (struct list_elem *before,
185 struct list_elem *first, struct list_elem *last)
186{
187 ASSERT (is_interior (before) || is_tail (before));
188 if (first == last)
189 return;
190 last = list_prev (last);
191
192 ASSERT (is_interior (first));
193 ASSERT (is_interior (last));
194
195 /* Cleanly remove FIRST...LAST from its current list. */
196 first->prev->next = last->next;
197 last->next->prev = first->prev;
198
199 /* Splice FIRST...LAST into new list. */
200 first->prev = before->prev;
201 last->next = before;
202 before->prev->next = first;
203 before->prev = last;
204}
205
206/* Inserts ELEM at the beginning of LIST, so that it becomes the
207 front in LIST. */
208void
209list_push_front (struct list *list, struct list_elem *elem)
210{
211 list_insert (list_begin (list), elem);
212}
213
214/* Inserts ELEM at the end of LIST, so that it becomes the
215 back in LIST. */
216void
217list_push_back (struct list *list, struct list_elem *elem)
218{
219 list_insert (list_end (list), elem);
220}
221
222/* Removes ELEM from its list and returns the element that
223 followed it. Undefined behavior if ELEM is not in a list.
224
225 A list element must be treated very carefully after removing
226 it from its list. Calling list_next() or list_prev() on ELEM
227 will return the item that was previously before or after ELEM,
228 but, e.g., list_prev(list_next(ELEM)) is no longer ELEM!
229
230 The list_remove() return value provides a convenient way to
231 iterate and remove elements from a list:
232
233 for (e = list_begin (&list); e != list_end (&list); e = list_remove (e))
234 {
235 ...do something with e...
236 }
237
238 If you need to free() elements of the list then you need to be
239 more conservative. Here's an alternate strategy that works
240 even in that case:
241
242 while (!list_empty (&list))
243 {
244 struct list_elem *e = list_pop_front (&list);
245 ...do something with e...
246 }
247*/
248struct list_elem *
249list_remove (struct list_elem *elem)
250{
251 ASSERT (is_interior (elem));
252 elem->prev->next = elem->next;
253 elem->next->prev = elem->prev;
254 return elem->next;
255}
256
257/* Removes the front element from LIST and returns it.
258 Undefined behavior if LIST is empty before removal. */
259struct list_elem *
260list_pop_front (struct list *list)
261{
262 struct list_elem *front = list_front (list);
263 list_remove (front);
264 return front;
265}
266
267/* Removes the back element from LIST and returns it.
268 Undefined behavior if LIST is empty before removal. */
269struct list_elem *
270list_pop_back (struct list *list)
271{
272 struct list_elem *back = list_back (list);
273 list_remove (back);
274 return back;
275}
276
277/* Returns the front element in LIST.
278 Undefined behavior if LIST is empty. */
279struct list_elem *
280list_front (struct list *list)
281{
282 ASSERT (!list_empty (list));
283 return list->head.next;
284}
285
286/* Returns the back element in LIST.
287 Undefined behavior if LIST is empty. */
288struct list_elem *
289list_back (struct list *list)
290{
291 ASSERT (!list_empty (list));
292 return list->tail.prev;
293}
294
295/* Returns the number of elements in LIST.
296 Runs in O(n) in the number of elements. */
297size_t
298list_size (struct list *list)
299{
300 struct list_elem *e;
301 size_t cnt = 0;
302
303 for (e = list_begin (list); e != list_end (list); e = list_next (e))
304 cnt++;
305 return cnt;
306}
307
308/* Returns true if LIST is empty, false otherwise. */
309bool
310list_empty (struct list *list)
311{
312 return list_begin (list) == list_end (list);
313}
314
315/* Swaps the `struct list_elem *'s that A and B point to. */
316static void
317swap (struct list_elem **a, struct list_elem **b)
318{
319 struct list_elem *t = *a;
320 *a = *b;
321 *b = t;
322}
323
324/* Reverses the order of LIST. */
325void
326list_reverse (struct list *list)
327{
328 if (!list_empty (list))
329 {
330 struct list_elem *e;
331
332 for (e = list_begin (list); e != list_end (list); e = e->prev)
333 swap (&e->prev, &e->next);
334 swap (&list->head.next, &list->tail.prev);
335 swap (&list->head.next->prev, &list->tail.prev->next);
336 }
337}
338
339/* Returns true only if the list elements A through B (exclusive)
340 are in order according to LESS given auxiliary data AUX. */
341static bool
342is_sorted (struct list_elem *a, struct list_elem *b,
343 list_less_func *less, void *aux)
344{
345 if (a != b)
346 while ((a = list_next (a)) != b)
347 if (less (a, list_prev (a), aux))
348 return false;
349 return true;
350}
351
352/* Finds a run, starting at A and ending not after B, of list
353 elements that are in nondecreasing order according to LESS
354 given auxiliary data AUX. Returns the (exclusive) end of the
355 run.
356 A through B (exclusive) must form a non-empty range. */
357static struct list_elem *
358find_end_of_run (struct list_elem *a, struct list_elem *b,
359 list_less_func *less, void *aux)
360{
361 ASSERT (a != NULL);
362 ASSERT (b != NULL);
363 ASSERT (less != NULL);
364 ASSERT (a != b);
365
366 do
367 {
368 a = list_next (a);
369 }
370 while (a != b && !less (a, list_prev (a), aux));
371 return a;
372}
373
374/* Merges A0 through A1B0 (exclusive) with A1B0 through B1
375 (exclusive) to form a combined range also ending at B1
376 (exclusive). Both input ranges must be nonempty and sorted in
377 nondecreasing order according to LESS given auxiliary data
378 AUX. The output range will be sorted the same way. */
379static void
380inplace_merge (struct list_elem *a0, struct list_elem *a1b0,
381 struct list_elem *b1,
382 list_less_func *less, void *aux)
383{
384 ASSERT (a0 != NULL);
385 ASSERT (a1b0 != NULL);
386 ASSERT (b1 != NULL);
387 ASSERT (less != NULL);
388 ASSERT (is_sorted (a0, a1b0, less, aux));
389 ASSERT (is_sorted (a1b0, b1, less, aux));
390
391 while (a0 != a1b0 && a1b0 != b1)
392 if (!less (a1b0, a0, aux))
393 a0 = list_next (a0);
394 else
395 {
396 a1b0 = list_next (a1b0);
397 list_splice (a0, list_prev (a1b0), a1b0);
398 }
399}
400
401/* Sorts LIST according to LESS given auxiliary data AUX, using a
402 natural iterative merge sort that runs in O(n lg n) time and
403 O(1) space in the number of elements in LIST. */
404void
405list_sort (struct list *list, list_less_func *less, void *aux)
406{
407 size_t output_run_cnt; /* Number of runs output in current pass. */
408
409 ASSERT (list != NULL);
410 ASSERT (less != NULL);
411
412 /* Pass over the list repeatedly, merging adjacent runs of
413 nondecreasing elements, until only one run is left. */
414 do
415 {
416 struct list_elem *a0; /* Start of first run. */
417 struct list_elem *a1b0; /* End of first run, start of second. */
418 struct list_elem *b1; /* End of second run. */
419
420 output_run_cnt = 0;
421 for (a0 = list_begin (list); a0 != list_end (list); a0 = b1)
422 {
423 /* Each iteration produces one output run. */
424 output_run_cnt++;
425
426 /* Locate two adjacent runs of nondecreasing elements
427 A0...A1B0 and A1B0...B1. */
428 a1b0 = find_end_of_run (a0, list_end (list), less, aux);
429 if (a1b0 == list_end (list))
430 break;
431 b1 = find_end_of_run (a1b0, list_end (list), less, aux);
432
433 /* Merge the runs. */
434 inplace_merge (a0, a1b0, b1, less, aux);
435 }
436 }
437 while (output_run_cnt > 1);
438
439 ASSERT (is_sorted (list_begin (list), list_end (list), less, aux));
440}
441
442/* Inserts ELEM in the proper position in LIST, which must be
443 sorted according to LESS given auxiliary data AUX.
444 Runs in O(n) average case in the number of elements in LIST. */
445void
446list_insert_ordered (struct list *list, struct list_elem *elem,
447 list_less_func *less, void *aux)
448{
449 struct list_elem *e;
450
451 ASSERT (list != NULL);
452 ASSERT (elem != NULL);
453 ASSERT (less != NULL);
454
455 for (e = list_begin (list); e != list_end (list); e = list_next (e))
456 if (less (elem, e, aux))
457 break;
458 return list_insert (e, elem);
459}
460
461/* Iterates through LIST and removes all but the first in each
462 set of adjacent elements that are equal according to LESS
463 given auxiliary data AUX. If DUPLICATES is non-null, then the
464 elements from LIST are appended to DUPLICATES. */
465void
466list_unique (struct list *list, struct list *duplicates,
467 list_less_func *less, void *aux)
468{
469 struct list_elem *elem, *next;
470
471 ASSERT (list != NULL);
472 ASSERT (less != NULL);
473 if (list_empty (list))
474 return;
475
476 elem = list_begin (list);
477 while ((next = list_next (elem)) != list_end (list))
478 if (!less (elem, next, aux) && !less (next, elem, aux))
479 {
480 list_remove (next);
481 if (duplicates != NULL)
482 list_push_back (duplicates, next);
483 }
484 else
485 elem = next;
486}
487
488/* Returns the element in LIST with the largest value according
489 to LESS given auxiliary data AUX. If there is more than one
490 maximum, returns the one that appears earlier in the list. If
491 the list is empty, returns its tail. */
492struct list_elem *
493list_max (struct list *list, list_less_func *less, void *aux)
494{
495 struct list_elem *max = list_begin (list);
496 if (max != list_end (list))
497 {
498 struct list_elem *e;
499
500 for (e = list_next (max); e != list_end (list); e = list_next (e))
501 if (less (max, e, aux))
502 max = e;
503 }
504 return max;
505}
506
507/* Returns the element in LIST with the smallest value according
508 to LESS given auxiliary data AUX. If there is more than one
509 minimum, returns the one that appears earlier in the list. If
510 the list is empty, returns its tail. */
511struct list_elem *
512list_min (struct list *list, list_less_func *less, void *aux)
513{
514 struct list_elem *min = list_begin (list);
515 if (min != list_end (list))
516 {
517 struct list_elem *e;
518
519 for (e = list_next (min); e != list_end (list); e = list_next (e))
520 if (less (e, min, aux))
521 min = e;
522 }
523 return min;
524}
diff --git a/pintos-progos/lib/kernel/list.h b/pintos-progos/lib/kernel/list.h
new file mode 100644
index 0000000..82efbb5
--- /dev/null
+++ b/pintos-progos/lib/kernel/list.h
@@ -0,0 +1,181 @@
1#ifndef __LIB_KERNEL_LIST_H
2#define __LIB_KERNEL_LIST_H
3
4/* Doubly linked list.
5
6 This implementation of a doubly linked list does not require
7 use of dynamically allocated memory. Instead, each structure
8 that is a potential list element must embed a struct list_elem
9 member. All of the list functions operate on these `struct
10 list_elem's. The list_entry macro allows conversion from a
11 struct list_elem back to a structure object that contains it.
12
13 For example, suppose there is a needed for a list of `struct
14 foo'. `struct foo' should contain a `struct list_elem'
15 member, like so:
16
17 struct foo
18 {
19 struct list_elem elem;
20 int bar;
21 ...other members...
22 };
23
24 Then a list of `struct foo' can be be declared and initialized
25 like so:
26
27 struct list foo_list;
28
29 list_init (&foo_list);
30
31 Iteration is a typical situation where it is necessary to
32 convert from a struct list_elem back to its enclosing
33 structure. Here's an example using foo_list:
34
35 struct list_elem *e;
36
37 for (e = list_begin (&foo_list); e != list_end (&foo_list);
38 e = list_next (e))
39 {
40 struct foo *f = list_entry (e, struct foo, elem);
41 ...do something with f...
42 }
43
44 You can find real examples of list usage throughout the
45 source; for example, malloc.c, palloc.c, and thread.c in the
46 threads directory all use lists.
47
48 The interface for this list is inspired by the list<> template
49 in the C++ STL. If you're familiar with list<>, you should
50 find this easy to use. However, it should be emphasized that
51 these lists do *no* type checking and can't do much other
52 correctness checking. If you screw up, it will bite you.
53
54 Glossary of list terms:
55
56 - "front": The first element in a list. Undefined in an
57 empty list. Returned by list_front().
58
59 - "back": The last element in a list. Undefined in an empty
60 list. Returned by list_back().
61
62 - "tail": The element figuratively just after the last
63 element of a list. Well defined even in an empty list.
64 Returned by list_end(). Used as the end sentinel for an
65 iteration from front to back.
66
67 - "beginning": In a non-empty list, the front. In an empty
68 list, the tail. Returned by list_begin(). Used as the
69 starting point for an iteration from front to back.
70
71 - "head": The element figuratively just before the first
72 element of a list. Well defined even in an empty list.
73 Returned by list_rend(). Used as the end sentinel for an
74 iteration from back to front.
75
76 - "reverse beginning": In a non-empty list, the back. In an
77 empty list, the head. Returned by list_rbegin(). Used as
78 the starting point for an iteration from back to front.
79
80 - "interior element": An element that is not the head or
81 tail, that is, a real list element. An empty list does
82 not have any interior elements.
83*/
84
85#include <stdbool.h>
86#include <stddef.h>
87#include <stdint.h>
88
89/* List element. */
90struct list_elem
91 {
92 struct list_elem *prev; /* Previous list element. */
93 struct list_elem *next; /* Next list element. */
94 };
95
96/* List. */
97struct list
98 {
99 struct list_elem head; /* List head. */
100 struct list_elem tail; /* List tail. */
101 };
102
103/* Converts pointer to list element LIST_ELEM into a pointer to
104 the structure that LIST_ELEM is embedded inside. Supply the
105 name of the outer structure STRUCT and the member name MEMBER
106 of the list element. See the big comment at the top of the
107 file for an example. */
108#define list_entry(LIST_ELEM, STRUCT, MEMBER) \
109 ((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next \
110 - offsetof (STRUCT, MEMBER.next)))
111
112/* List initialization.
113
114 A list may be initialized by calling list_init():
115
116 struct list my_list;
117 list_init (&my_list);
118
119 or with an initializer using LIST_INITIALIZER:
120
121 struct list my_list = LIST_INITIALIZER (my_list); */
122#define LIST_INITIALIZER(NAME) { { NULL, &(NAME).tail }, \
123 { &(NAME).head, NULL } }
124
125void list_init (struct list *);
126
127/* List traversal. */
128struct list_elem *list_begin (struct list *);
129struct list_elem *list_next (struct list_elem *);
130struct list_elem *list_end (struct list *);
131
132struct list_elem *list_rbegin (struct list *);
133struct list_elem *list_prev (struct list_elem *);
134struct list_elem *list_rend (struct list *);
135
136struct list_elem *list_head (struct list *);
137struct list_elem *list_tail (struct list *);
138
139/* List insertion. */
140void list_insert (struct list_elem *, struct list_elem *);
141void list_splice (struct list_elem *before,
142 struct list_elem *first, struct list_elem *last);
143void list_push_front (struct list *, struct list_elem *);
144void list_push_back (struct list *, struct list_elem *);
145
146/* List removal. */
147struct list_elem *list_remove (struct list_elem *);
148struct list_elem *list_pop_front (struct list *);
149struct list_elem *list_pop_back (struct list *);
150
151/* List elements. */
152struct list_elem *list_front (struct list *);
153struct list_elem *list_back (struct list *);
154
155/* List properties. */
156size_t list_size (struct list *);
157bool list_empty (struct list *);
158
159/* Miscellaneous. */
160void list_reverse (struct list *);
161
162/* Compares the value of two list elements A and B, given
163 auxiliary data AUX. Returns true if A is less than B, or
164 false if A is greater than or equal to B. */
165typedef bool list_less_func (const struct list_elem *a,
166 const struct list_elem *b,
167 void *aux);
168
169/* Operations on lists with ordered elements. */
170void list_sort (struct list *,
171 list_less_func *, void *aux);
172void list_insert_ordered (struct list *, struct list_elem *,
173 list_less_func *, void *aux);
174void list_unique (struct list *, struct list *duplicates,
175 list_less_func *, void *aux);
176
177/* Max and min. */
178struct list_elem *list_max (struct list *, list_less_func *, void *aux);
179struct list_elem *list_min (struct list *, list_less_func *, void *aux);
180
181#endif /* lib/kernel/list.h */
diff --git a/pintos-progos/lib/kernel/stdio.h b/pintos-progos/lib/kernel/stdio.h
new file mode 100644
index 0000000..3e5bae9
--- /dev/null
+++ b/pintos-progos/lib/kernel/stdio.h
@@ -0,0 +1,6 @@
1#ifndef __LIB_KERNEL_STDIO_H
2#define __LIB_KERNEL_STDIO_H
3
4void putbuf (const char *, size_t);
5
6#endif /* lib/kernel/stdio.h */
diff --git a/pintos-progos/lib/limits.h b/pintos-progos/lib/limits.h
new file mode 100644
index 0000000..c957ec4
--- /dev/null
+++ b/pintos-progos/lib/limits.h
@@ -0,0 +1,34 @@
1#ifndef __LIB_LIMITS_H
2#define __LIB_LIMITS_H
3
4#define CHAR_BIT 8
5
6#define SCHAR_MAX 127
7#define SCHAR_MIN (-SCHAR_MAX - 1)
8#define UCHAR_MAX 255
9
10#ifdef __CHAR_UNSIGNED__
11#define CHAR_MIN 0
12#define CHAR_MAX UCHAR_MAX
13#else
14#define CHAR_MIN SCHAR_MIN
15#define CHAR_MAX SCHAR_MAX
16#endif
17
18#define SHRT_MAX 32767
19#define SHRT_MIN (-SHRT_MAX - 1)
20#define USHRT_MAX 65535
21
22#define INT_MAX 2147483647
23#define INT_MIN (-INT_MAX - 1)
24#define UINT_MAX 4294967295U
25
26#define LONG_MAX 2147483647L
27#define LONG_MIN (-LONG_MAX - 1)
28#define ULONG_MAX 4294967295UL
29
30#define LLONG_MAX 9223372036854775807LL
31#define LLONG_MIN (-LLONG_MAX - 1)
32#define ULLONG_MAX 18446744073709551615ULL
33
34#endif /* lib/limits.h */
diff --git a/pintos-progos/lib/packed.h b/pintos-progos/lib/packed.h
new file mode 100644
index 0000000..9a9b6e2
--- /dev/null
+++ b/pintos-progos/lib/packed.h
@@ -0,0 +1,10 @@
1#ifndef __LIB_PACKED_H
2#define __LIB_PACKED_H
3
4/* The "packed" attribute, when applied to a structure, prevents
5 GCC from inserting padding bytes between or after structure
6 members. It must be specified at the time of the structure's
7 definition, normally just after the closing brace. */
8#define PACKED __attribute__ ((packed))
9
10#endif /* lib/packed.h */
diff --git a/pintos-progos/lib/random.c b/pintos-progos/lib/random.c
new file mode 100644
index 0000000..a4761b6
--- /dev/null
+++ b/pintos-progos/lib/random.c
@@ -0,0 +1,83 @@
1#include "random.h"
2#include <stdbool.h>
3#include <stdint.h>
4#include "debug.h"
5
6/* RC4-based pseudo-random number generator (PRNG).
7
8 RC4 is a stream cipher. We're not using it here for its
9 cryptographic properties, but because it is easy to implement
10 and its output is plenty random for non-cryptographic
11 purposes.
12
13 See http://en.wikipedia.org/wiki/RC4_(cipher) for information
14 on RC4.*/
15
16/* RC4 state. */
17static uint8_t s[256]; /* S[]. */
18static uint8_t s_i, s_j; /* i, j. */
19
20/* Already initialized? */
21static bool inited;
22
23/* Swaps the bytes pointed to by A and B. */
24static inline void
25swap_byte (uint8_t *a, uint8_t *b)
26{
27 uint8_t t = *a;
28 *a = *b;
29 *b = t;
30}
31
32/* Initializes or reinitializes the PRNG with the given SEED. */
33void
34random_init (unsigned seed)
35{
36 uint8_t *seedp = (uint8_t *) &seed;
37 int i;
38 uint8_t j;
39
40 for (i = 0; i < 256; i++)
41 s[i] = i;
42 for (i = j = 0; i < 256; i++)
43 {
44 j += s[i] + seedp[i % sizeof seed];
45 swap_byte (s + i, s + j);
46 }
47
48 s_i = s_j = 0;
49 inited = true;
50}
51
52/* Writes SIZE random bytes into BUF. */
53void
54random_bytes (void *buf_, size_t size)
55{
56 uint8_t *buf;
57
58 if (!inited)
59 random_init (0);
60
61 for (buf = buf_; size-- > 0; buf++)
62 {
63 uint8_t s_k;
64
65 s_i++;
66 s_j += s[s_i];
67 swap_byte (s + s_i, s + s_j);
68
69 s_k = s[s_i] + s[s_j];
70 *buf = s[s_k];
71 }
72}
73
74/* Returns a pseudo-random unsigned long.
75 Use random_ulong() % n to obtain a random number in the range
76 0...n (exclusive). */
77unsigned long
78random_ulong (void)
79{
80 unsigned long ul;
81 random_bytes (&ul, sizeof ul);
82 return ul;
83}
diff --git a/pintos-progos/lib/random.h b/pintos-progos/lib/random.h
new file mode 100644
index 0000000..0950ae2
--- /dev/null
+++ b/pintos-progos/lib/random.h
@@ -0,0 +1,10 @@
1#ifndef __LIB_RANDOM_H
2#define __LIB_RANDOM_H
3
4#include <stddef.h>
5
6void random_init (unsigned seed);
7void random_bytes (void *, size_t);
8unsigned long random_ulong (void);
9
10#endif /* lib/random.h */
diff --git a/pintos-progos/lib/round.h b/pintos-progos/lib/round.h
new file mode 100644
index 0000000..3aa6642
--- /dev/null
+++ b/pintos-progos/lib/round.h
@@ -0,0 +1,18 @@
1#ifndef __LIB_ROUND_H
2#define __LIB_ROUND_H
3
4/* Yields X rounded up to the nearest multiple of STEP.
5 For X >= 0, STEP >= 1 only. */
6#define ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP) * (STEP))
7
8/* Yields X divided by STEP, rounded up.
9 For X >= 0, STEP >= 1 only. */
10#define DIV_ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP))
11
12/* Yields X rounded down to the nearest multiple of STEP.
13 For X >= 0, STEP >= 1 only. */
14#define ROUND_DOWN(X, STEP) ((X) / (STEP) * (STEP))
15
16/* There is no DIV_ROUND_DOWN. It would be simply X / STEP. */
17
18#endif /* lib/round.h */
diff --git a/pintos-progos/lib/stdarg.h b/pintos-progos/lib/stdarg.h
new file mode 100644
index 0000000..32622b5
--- /dev/null
+++ b/pintos-progos/lib/stdarg.h
@@ -0,0 +1,14 @@
1#ifndef __LIB_STDARG_H
2#define __LIB_STDARG_H
3
4/* GCC has <stdarg.h> functionality as built-ins,
5 so all we need is to use it. */
6
7typedef __builtin_va_list va_list;
8
9#define va_start(LIST, ARG) __builtin_va_start (LIST, ARG)
10#define va_end(LIST) __builtin_va_end (LIST)
11#define va_arg(LIST, TYPE) __builtin_va_arg (LIST, TYPE)
12#define va_copy(DST, SRC) __builtin_va_copy (DST, SRC)
13
14#endif /* lib/stdarg.h */
diff --git a/pintos-progos/lib/stdbool.h b/pintos-progos/lib/stdbool.h
new file mode 100644
index 0000000..f173a91
--- /dev/null
+++ b/pintos-progos/lib/stdbool.h
@@ -0,0 +1,9 @@
1#ifndef __LIB_STDBOOL_H
2#define __LIB_STDBOOL_H
3
4#define bool _Bool
5#define true 1
6#define false 0
7#define __bool_true_false_are_defined 1
8
9#endif /* lib/stdbool.h */
diff --git a/pintos-progos/lib/stddef.h b/pintos-progos/lib/stddef.h
new file mode 100644
index 0000000..4e74fa6
--- /dev/null
+++ b/pintos-progos/lib/stddef.h
@@ -0,0 +1,12 @@
1#ifndef __LIB_STDDEF_H
2#define __LIB_STDDEF_H
3
4#define NULL ((void *) 0)
5#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *) 0)->MEMBER)
6
7/* GCC predefines the types we need for ptrdiff_t and size_t,
8 so that we don't have to guess. */
9typedef __PTRDIFF_TYPE__ ptrdiff_t;
10typedef __SIZE_TYPE__ size_t;
11
12#endif /* lib/stddef.h */
diff --git a/pintos-progos/lib/stdint.h b/pintos-progos/lib/stdint.h
new file mode 100644
index 0000000..ef5f214
--- /dev/null
+++ b/pintos-progos/lib/stdint.h
@@ -0,0 +1,51 @@
1#ifndef __LIB_STDINT_H
2#define __LIB_STDINT_H
3
4typedef signed char int8_t;
5#define INT8_MAX 127
6#define INT8_MIN (-INT8_MAX - 1)
7
8typedef signed short int int16_t;
9#define INT16_MAX 32767
10#define INT16_MIN (-INT16_MAX - 1)
11
12typedef signed int int32_t;
13#define INT32_MAX 2147483647
14#define INT32_MIN (-INT32_MAX - 1)
15
16typedef signed long long int int64_t;
17#define INT64_MAX 9223372036854775807LL
18#define INT64_MIN (-INT64_MAX - 1)
19
20typedef unsigned char uint8_t;
21#define UINT8_MAX 255
22
23typedef unsigned short int uint16_t;
24#define UINT16_MAX 65535
25
26typedef unsigned int uint32_t;
27#define UINT32_MAX 4294967295U
28
29typedef unsigned long long int uint64_t;
30#define UINT64_MAX 18446744073709551615ULL
31
32typedef int32_t intptr_t;
33#define INTPTR_MIN INT32_MIN
34#define INTPTR_MAX INT32_MAX
35
36typedef uint32_t uintptr_t;
37#define UINTPTR_MAX UINT32_MAX
38
39typedef int64_t intmax_t;
40#define INTMAX_MIN INT64_MIN
41#define INTMAX_MAX INT64_MAX
42
43typedef uint64_t uintmax_t;
44#define UINTMAX_MAX UINT64_MAX
45
46#define PTRDIFF_MIN INT32_MIN
47#define PTRDIFF_MAX INT32_MAX
48
49#define SIZE_MAX UINT32_MAX
50
51#endif /* lib/stdint.h */
diff --git a/pintos-progos/lib/stdio.c b/pintos-progos/lib/stdio.c
new file mode 100644
index 0000000..8927c50
--- /dev/null
+++ b/pintos-progos/lib/stdio.c
@@ -0,0 +1,655 @@
1#include <stdio.h>
2#include <ctype.h>
3#include <inttypes.h>
4#include <round.h>
5#include <stdint.h>
6#include <string.h>
7
8/* Auxiliary data for vsnprintf_helper(). */
9struct vsnprintf_aux
10 {
11 char *p; /* Current output position. */
12 int length; /* Length of output string. */
13 int max_length; /* Max length of output string. */
14 };
15
16static void vsnprintf_helper (char, void *);
17
18/* Like vprintf(), except that output is stored into BUFFER,
19 which must have space for BUF_SIZE characters. Writes at most
20 BUF_SIZE - 1 characters to BUFFER, followed by a null
21 terminator. BUFFER will always be null-terminated unless
22 BUF_SIZE is zero. Returns the number of characters that would
23 have been written to BUFFER, not including a null terminator,
24 had there been enough room. */
25int
26vsnprintf (char *buffer, size_t buf_size, const char *format, va_list args)
27{
28 /* Set up aux data for vsnprintf_helper(). */
29 struct vsnprintf_aux aux;
30 aux.p = buffer;
31 aux.length = 0;
32 aux.max_length = buf_size > 0 ? buf_size - 1 : 0;
33
34 /* Do most of the work. */
35 __vprintf (format, args, vsnprintf_helper, &aux);
36
37 /* Add null terminator. */
38 if (buf_size > 0)
39 *aux.p = '\0';
40
41 return aux.length;
42}
43
44/* Helper function for vsnprintf(). */
45static void
46vsnprintf_helper (char ch, void *aux_)
47{
48 struct vsnprintf_aux *aux = aux_;
49
50 if (aux->length++ < aux->max_length)
51 *aux->p++ = ch;
52}
53
54/* Like printf(), except that output is stored into BUFFER,
55 which must have space for BUF_SIZE characters. Writes at most
56 BUF_SIZE - 1 characters to BUFFER, followed by a null
57 terminator. BUFFER will always be null-terminated unless
58 BUF_SIZE is zero. Returns the number of characters that would
59 have been written to BUFFER, not including a null terminator,
60 had there been enough room. */
61int
62snprintf (char *buffer, size_t buf_size, const char *format, ...)
63{
64 va_list args;
65 int retval;
66
67 va_start (args, format);
68 retval = vsnprintf (buffer, buf_size, format, args);
69 va_end (args);
70
71 return retval;
72}
73
74/* Writes formatted output to the console.
75 In the kernel, the console is both the video display and first
76 serial port.
77 In userspace, the console is file descriptor 1. */
78int
79printf (const char *format, ...)
80{
81 va_list args;
82 int retval;
83
84 va_start (args, format);
85 retval = vprintf (format, args);
86 va_end (args);
87
88 return retval;
89}
90
91/* printf() formatting internals. */
92
93/* A printf() conversion. */
94struct printf_conversion
95 {
96 /* Flags. */
97 enum
98 {
99 MINUS = 1 << 0, /* '-' */
100 PLUS = 1 << 1, /* '+' */
101 SPACE = 1 << 2, /* ' ' */
102 POUND = 1 << 3, /* '#' */
103 ZERO = 1 << 4, /* '0' */
104 GROUP = 1 << 5 /* '\'' */
105 }
106 flags;
107
108 /* Minimum field width. */
109 int width;
110
111 /* Numeric precision.
112 -1 indicates no precision was specified. */
113 int precision;
114
115 /* Type of argument to format. */
116 enum
117 {
118 CHAR = 1, /* hh */
119 SHORT = 2, /* h */
120 INT = 3, /* (none) */
121 INTMAX = 4, /* j */
122 LONG = 5, /* l */
123 LONGLONG = 6, /* ll */
124 PTRDIFFT = 7, /* t */
125 SIZET = 8 /* z */
126 }
127 type;
128 };
129
130struct integer_base
131 {
132 int base; /* Base. */
133 const char *digits; /* Collection of digits. */
134 int x; /* `x' character to use, for base 16 only. */
135 int group; /* Number of digits to group with ' flag. */
136 };
137
138static const struct integer_base base_d = {10, "0123456789", 0, 3};
139static const struct integer_base base_o = {8, "01234567", 0, 3};
140static const struct integer_base base_x = {16, "0123456789abcdef", 'x', 4};
141static const struct integer_base base_X = {16, "0123456789ABCDEF", 'X', 4};
142
143static const char *parse_conversion (const char *format,
144 struct printf_conversion *,
145 va_list *);
146static void format_integer (uintmax_t value, bool is_signed, bool negative,
147 const struct integer_base *,
148 const struct printf_conversion *,
149 void (*output) (char, void *), void *aux);
150static void output_dup (char ch, size_t cnt,
151 void (*output) (char, void *), void *aux);
152static void format_string (const char *string, int length,
153 struct printf_conversion *,
154 void (*output) (char, void *), void *aux);
155
156void
157__vprintf (const char *format, va_list args,
158 void (*output) (char, void *), void *aux)
159{
160 for (; *format != '\0'; format++)
161 {
162 struct printf_conversion c;
163
164 /* Literally copy non-conversions to output. */
165 if (*format != '%')
166 {
167 output (*format, aux);
168 continue;
169 }
170 format++;
171
172 /* %% => %. */
173 if (*format == '%')
174 {
175 output ('%', aux);
176 continue;
177 }
178
179 /* Parse conversion specifiers. */
180 format = parse_conversion (format, &c, &args);
181
182 /* Do conversion. */
183 switch (*format)
184 {
185 case 'd':
186 case 'i':
187 {
188 /* Signed integer conversions. */
189 intmax_t value;
190
191 switch (c.type)
192 {
193 case CHAR:
194 value = (signed char) va_arg (args, int);
195 break;
196 case SHORT:
197 value = (short) va_arg (args, int);
198 break;
199 case INT:
200 value = va_arg (args, int);
201 break;
202 case INTMAX:
203 value = va_arg (args, intmax_t);
204 break;
205 case LONG:
206 value = va_arg (args, long);
207 break;
208 case LONGLONG:
209 value = va_arg (args, long long);
210 break;
211 case PTRDIFFT:
212 value = va_arg (args, ptrdiff_t);
213 break;
214 case SIZET:
215 value = va_arg (args, size_t);
216 if (value > SIZE_MAX / 2)
217 value = value - SIZE_MAX - 1;
218 break;
219 default:
220 NOT_REACHED ();
221 }
222
223 format_integer (value < 0 ? -value : value,
224 true, value < 0, &base_d, &c, output, aux);
225 }
226 break;
227
228 case 'o':
229 case 'u':
230 case 'x':
231 case 'X':
232 {
233 /* Unsigned integer conversions. */
234 uintmax_t value;
235 const struct integer_base *b;
236
237 switch (c.type)
238 {
239 case CHAR:
240 value = (unsigned char) va_arg (args, unsigned);
241 break;
242 case SHORT:
243 value = (unsigned short) va_arg (args, unsigned);
244 break;
245 case INT:
246 value = va_arg (args, unsigned);
247 break;
248 case INTMAX:
249 value = va_arg (args, uintmax_t);
250 break;
251 case LONG:
252 value = va_arg (args, unsigned long);
253 break;
254 case LONGLONG:
255 value = va_arg (args, unsigned long long);
256 break;
257 case PTRDIFFT:
258 value = va_arg (args, ptrdiff_t);
259#if UINTMAX_MAX != PTRDIFF_MAX
260 value &= ((uintmax_t) PTRDIFF_MAX << 1) | 1;
261#endif
262 break;
263 case SIZET:
264 value = va_arg (args, size_t);
265 break;
266 default:
267 NOT_REACHED ();
268 }
269
270 switch (*format)
271 {
272 case 'o': b = &base_o; break;
273 case 'u': b = &base_d; break;
274 case 'x': b = &base_x; break;
275 case 'X': b = &base_X; break;
276 default: NOT_REACHED ();
277 }
278
279 format_integer (value, false, false, b, &c, output, aux);
280 }
281 break;
282
283 case 'c':
284 {
285 /* Treat character as single-character string. */
286 char ch = va_arg (args, int);
287 format_string (&ch, 1, &c, output, aux);
288 }
289 break;
290
291 case 's':
292 {
293 /* String conversion. */
294 const char *s = va_arg (args, char *);
295 if (s == NULL)
296 s = "(null)";
297
298 /* Limit string length according to precision.
299 Note: if c.precision == -1 then strnlen() will get
300 SIZE_MAX for MAXLEN, which is just what we want. */
301 format_string (s, strnlen (s, c.precision), &c, output, aux);
302 }
303 break;
304
305 case 'p':
306 {
307 /* Pointer conversion.
308 Format pointers as %#x. */
309 void *p = va_arg (args, void *);
310
311 c.flags = POUND;
312 format_integer ((uintptr_t) p, false, false,
313 &base_x, &c, output, aux);
314 }
315 break;
316
317 case 'f':
318 case 'e':
319 case 'E':
320 case 'g':
321 case 'G':
322 case 'n':
323 /* We don't support floating-point arithmetic,
324 and %n can be part of a security hole. */
325 __printf ("<<no %%%c in kernel>>", output, aux, *format);
326 break;
327
328 default:
329 __printf ("<<no %%%c conversion>>", output, aux, *format);
330 break;
331 }
332 }
333}
334
335/* Parses conversion option characters starting at FORMAT and
336 initializes C appropriately. Returns the character in FORMAT
337 that indicates the conversion (e.g. the `d' in `%d'). Uses
338 *ARGS for `*' field widths and precisions. */
339static const char *
340parse_conversion (const char *format, struct printf_conversion *c,
341 va_list *args)
342{
343 /* Parse flag characters. */
344 c->flags = 0;
345 for (;;)
346 {
347 switch (*format++)
348 {
349 case '-':
350 c->flags |= MINUS;
351 break;
352 case '+':
353 c->flags |= PLUS;
354 break;
355 case ' ':
356 c->flags |= SPACE;
357 break;
358 case '#':
359 c->flags |= POUND;
360 break;
361 case '0':
362 c->flags |= ZERO;
363 break;
364 case '\'':
365 c->flags |= GROUP;
366 break;
367 default:
368 format--;
369 goto not_a_flag;
370 }
371 }
372 not_a_flag:
373 if (c->flags & MINUS)
374 c->flags &= ~ZERO;
375 if (c->flags & PLUS)
376 c->flags &= ~SPACE;
377
378 /* Parse field width. */
379 c->width = 0;
380 if (*format == '*')
381 {
382 format++;
383 c->width = va_arg (*args, int);
384 }
385 else
386 {
387 for (; isdigit (*format); format++)
388 c->width = c->width * 10 + *format - '0';
389 }
390 if (c->width < 0)
391 {
392 c->width = -c->width;
393 c->flags |= MINUS;
394 }
395
396 /* Parse precision. */
397 c->precision = -1;
398 if (*format == '.')
399 {
400 format++;
401 if (*format == '*')
402 {
403 format++;
404 c->precision = va_arg (*args, int);
405 }
406 else
407 {
408 c->precision = 0;
409 for (; isdigit (*format); format++)
410 c->precision = c->precision * 10 + *format - '0';
411 }
412 if (c->precision < 0)
413 c->precision = -1;
414 }
415 if (c->precision >= 0)
416 c->flags &= ~ZERO;
417
418 /* Parse type. */
419 c->type = INT;
420 switch (*format++)
421 {
422 case 'h':
423 if (*format == 'h')
424 {
425 format++;
426 c->type = CHAR;
427 }
428 else
429 c->type = SHORT;
430 break;
431
432 case 'j':
433 c->type = INTMAX;
434 break;
435
436 case 'l':
437 if (*format == 'l')
438 {
439 format++;
440 c->type = LONGLONG;
441 }
442 else
443 c->type = LONG;
444 break;
445
446 case 't':
447 c->type = PTRDIFFT;
448 break;
449
450 case 'z':
451 c->type = SIZET;
452 break;
453
454 default:
455 format--;
456 break;
457 }
458
459 return format;
460}
461
462/* Performs an integer conversion, writing output to OUTPUT with
463 auxiliary data AUX. The integer converted has absolute value
464 VALUE. If IS_SIGNED is true, does a signed conversion with
465 NEGATIVE indicating a negative value; otherwise does an
466 unsigned conversion and ignores NEGATIVE. The output is done
467 according to the provided base B. Details of the conversion
468 are in C. */
469static void
470format_integer (uintmax_t value, bool is_signed, bool negative,
471 const struct integer_base *b,
472 const struct printf_conversion *c,
473 void (*output) (char, void *), void *aux)
474{
475 char buf[64], *cp; /* Buffer and current position. */
476 int x; /* `x' character to use or 0 if none. */
477 int sign; /* Sign character or 0 if none. */
478 int precision; /* Rendered precision. */
479 int pad_cnt; /* # of pad characters to fill field width. */
480 int digit_cnt; /* # of digits output so far. */
481
482 /* Determine sign character, if any.
483 An unsigned conversion will never have a sign character,
484 even if one of the flags requests one. */
485 sign = 0;
486 if (is_signed)
487 {
488 if (c->flags & PLUS)
489 sign = negative ? '-' : '+';
490 else if (c->flags & SPACE)
491 sign = negative ? '-' : ' ';
492 else if (negative)
493 sign = '-';
494 }
495
496 /* Determine whether to include `0x' or `0X'.
497 It will only be included with a hexadecimal conversion of a
498 nonzero value with the # flag. */
499 x = (c->flags & POUND) && value ? b->x : 0;
500
501 /* Accumulate digits into buffer.
502 This algorithm produces digits in reverse order, so later we
503 will output the buffer's content in reverse. */
504 cp = buf;
505 digit_cnt = 0;
506 while (value > 0)
507 {
508 if ((c->flags & GROUP) && digit_cnt > 0 && digit_cnt % b->group == 0)
509 *cp++ = ',';
510 *cp++ = b->digits[value % b->base];
511 value /= b->base;
512 digit_cnt++;
513 }
514
515 /* Append enough zeros to match precision.
516 If requested precision is 0, then a value of zero is
517 rendered as a null string, otherwise as "0".
518 If the # flag is used with base 8, the result must always
519 begin with a zero. */
520 precision = c->precision < 0 ? 1 : c->precision;
521 while (cp - buf < precision && cp < buf + sizeof buf - 1)
522 *cp++ = '0';
523 if ((c->flags & POUND) && b->base == 8 && (cp == buf || cp[-1] != '0'))
524 *cp++ = '0';
525
526 /* Calculate number of pad characters to fill field width. */
527 pad_cnt = c->width - (cp - buf) - (x ? 2 : 0) - (sign != 0);
528 if (pad_cnt < 0)
529 pad_cnt = 0;
530
531 /* Do output. */
532 if ((c->flags & (MINUS | ZERO)) == 0)
533 output_dup (' ', pad_cnt, output, aux);
534 if (sign)
535 output (sign, aux);
536 if (x)
537 {
538 output ('0', aux);
539 output (x, aux);
540 }
541 if (c->flags & ZERO)
542 output_dup ('0', pad_cnt, output, aux);
543 while (cp > buf)
544 output (*--cp, aux);
545 if (c->flags & MINUS)
546 output_dup (' ', pad_cnt, output, aux);
547}
548
549/* Writes CH to OUTPUT with auxiliary data AUX, CNT times. */
550static void
551output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux)
552{
553 while (cnt-- > 0)
554 output (ch, aux);
555}
556
557/* Formats the LENGTH characters starting at STRING according to
558 the conversion specified in C. Writes output to OUTPUT with
559 auxiliary data AUX. */
560static void
561format_string (const char *string, int length,
562 struct printf_conversion *c,
563 void (*output) (char, void *), void *aux)
564{
565 int i;
566 if (c->width > length && (c->flags & MINUS) == 0)
567 output_dup (' ', c->width - length, output, aux);
568 for (i = 0; i < length; i++)
569 output (string[i], aux);
570 if (c->width > length && (c->flags & MINUS) != 0)
571 output_dup (' ', c->width - length, output, aux);
572}
573
574/* Wrapper for __vprintf() that converts varargs into a
575 va_list. */
576void
577__printf (const char *format,
578 void (*output) (char, void *), void *aux, ...)
579{
580 va_list args;
581
582 va_start (args, aux);
583 __vprintf (format, args, output, aux);
584 va_end (args);
585}
586
587/* Dumps the SIZE bytes in BUF to the console as hex bytes
588 arranged 16 per line. Numeric offsets are also included,
589 starting at OFS for the first byte in BUF. If ASCII is true
590 then the corresponding ASCII characters are also rendered
591 alongside. */
592void
593hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii)
594{
595 const uint8_t *buf = buf_;
596 const size_t per_line = 16; /* Maximum bytes per line. */
597
598 while (size > 0)
599 {
600 size_t start, end, n;
601 size_t i;
602
603 /* Number of bytes on this line. */
604 start = ofs % per_line;
605 end = per_line;
606 if (end - start > size)
607 end = start + size;
608 n = end - start;
609
610 /* Print line. */
611 printf ("%08jx ", (uintmax_t) ROUND_DOWN (ofs, per_line));
612 for (i = 0; i < start; i++)
613 printf (" ");
614 for (; i < end; i++)
615 printf ("%02hhx%c",
616 buf[i - start], i == per_line / 2 - 1? '-' : ' ');
617 if (ascii)
618 {
619 for (; i < per_line; i++)
620 printf (" ");
621 printf ("|");
622 for (i = 0; i < start; i++)
623 printf (" ");
624 for (; i < end; i++)
625 printf ("%c",
626 isprint (buf[i - start]) ? buf[i - start] : '.');
627 for (; i < per_line; i++)
628 printf (" ");
629 printf ("|");
630 }
631 printf ("\n");
632
633 ofs += n;
634 buf += n;
635 size -= n;
636 }
637}
638
639/* Prints SIZE, which represents a number of bytes, in a
640 human-readable format, e.g. "256 kB". */
641void
642print_human_readable_size (uint64_t size)
643{
644 if (size == 1)
645 printf ("1 byte");
646 else
647 {
648 static const char *factors[] = {"bytes", "kB", "MB", "GB", "TB", NULL};
649 const char **fp;
650
651 for (fp = factors; size >= 1024 && fp[1] != NULL; fp++)
652 size /= 1024;
653 printf ("%"PRIu64" %s", size, *fp);
654 }
655}
diff --git a/pintos-progos/lib/stdio.h b/pintos-progos/lib/stdio.h
new file mode 100644
index 0000000..2739c0a
--- /dev/null
+++ b/pintos-progos/lib/stdio.h
@@ -0,0 +1,40 @@
1#ifndef __LIB_STDIO_H
2#define __LIB_STDIO_H
3
4#include <debug.h>
5#include <stdarg.h>
6#include <stdbool.h>
7#include <stddef.h>
8#include <stdint.h>
9
10/* Include lib/user/stdio.h or lib/kernel/stdio.h, as
11 appropriate. */
12#include_next <stdio.h>
13
14/* Predefined file handles. */
15#define STDIN_FILENO 0
16#define STDOUT_FILENO 1
17
18/* Standard functions. */
19int printf (const char *, ...) PRINTF_FORMAT (1, 2);
20int snprintf (char *, size_t, const char *, ...) PRINTF_FORMAT (3, 4);
21int vprintf (const char *, va_list) PRINTF_FORMAT (1, 0);
22int vsnprintf (char *, size_t, const char *, va_list) PRINTF_FORMAT (3, 0);
23int putchar (int);
24int puts (const char *);
25
26/* Nonstandard functions. */
27void hex_dump (uintptr_t ofs, const void *, size_t size, bool ascii);
28void print_human_readable_size (uint64_t sz);
29
30/* Internal functions. */
31void __vprintf (const char *format, va_list args,
32 void (*output) (char, void *), void *aux);
33void __printf (const char *format,
34 void (*output) (char, void *), void *aux, ...);
35
36/* Try to be helpful. */
37#define sprintf dont_use_sprintf_use_snprintf
38#define vsprintf dont_use_vsprintf_use_vsnprintf
39
40#endif /* lib/stdio.h */
diff --git a/pintos-progos/lib/stdlib.c b/pintos-progos/lib/stdlib.c
new file mode 100644
index 0000000..84c7f61
--- /dev/null
+++ b/pintos-progos/lib/stdlib.c
@@ -0,0 +1,208 @@
1#include <ctype.h>
2#include <debug.h>
3#include <random.h>
4#include <stdlib.h>
5#include <stdbool.h>
6
7/* Converts a string representation of a signed decimal integer
8 in S into an `int', which is returned. */
9int
10atoi (const char *s)
11{
12 bool negative;
13 int value;
14
15 ASSERT (s != NULL);
16
17 /* Skip white space. */
18 while (isspace ((unsigned char) *s))
19 s++;
20
21 /* Parse sign. */
22 negative = false;
23 if (*s == '+')
24 s++;
25 else if (*s == '-')
26 {
27 negative = true;
28 s++;
29 }
30
31 /* Parse digits. We always initially parse the value as
32 negative, and then make it positive later, because the
33 negative range of an int is bigger than the positive range
34 on a 2's complement system. */
35 for (value = 0; isdigit (*s); s++)
36 value = value * 10 - (*s - '0');
37 if (!negative)
38 value = -value;
39
40 return value;
41}
42
43/* Compares A and B by calling the AUX function. */
44static int
45compare_thunk (const void *a, const void *b, void *aux)
46{
47 int (**compare) (const void *, const void *) = aux;
48 return (*compare) (a, b);
49}
50
51/* Sorts ARRAY, which contains CNT elements of SIZE bytes each,
52 using COMPARE. When COMPARE is passed a pair of elements A
53 and B, respectively, it must return a strcmp()-type result,
54 i.e. less than zero if A < B, zero if A == B, greater than
55 zero if A > B. Runs in O(n lg n) time and O(1) space in
56 CNT. */
57void
58qsort (void *array, size_t cnt, size_t size,
59 int (*compare) (const void *, const void *))
60{
61 sort (array, cnt, size, compare_thunk, &compare);
62}
63
64/* Swaps elements with 1-based indexes A_IDX and B_IDX in ARRAY
65 with elements of SIZE bytes each. */
66static void
67do_swap (unsigned char *array, size_t a_idx, size_t b_idx, size_t size)
68{
69 unsigned char *a = array + (a_idx - 1) * size;
70 unsigned char *b = array + (b_idx - 1) * size;
71 size_t i;
72
73 for (i = 0; i < size; i++)
74 {
75 unsigned char t = a[i];
76 a[i] = b[i];
77 b[i] = t;
78 }
79}
80
81/* Compares elements with 1-based indexes A_IDX and B_IDX in
82 ARRAY with elements of SIZE bytes each, using COMPARE to
83 compare elements, passing AUX as auxiliary data, and returns a
84 strcmp()-type result. */
85static int
86do_compare (unsigned char *array, size_t a_idx, size_t b_idx, size_t size,
87 int (*compare) (const void *, const void *, void *aux),
88 void *aux)
89{
90 return compare (array + (a_idx - 1) * size, array + (b_idx - 1) * size, aux);
91}
92
93/* "Float down" the element with 1-based index I in ARRAY of CNT
94 elements of SIZE bytes each, using COMPARE to compare
95 elements, passing AUX as auxiliary data. */
96static void
97heapify (unsigned char *array, size_t i, size_t cnt, size_t size,
98 int (*compare) (const void *, const void *, void *aux),
99 void *aux)
100{
101 for (;;)
102 {
103 /* Set `max' to the index of the largest element among I
104 and its children (if any). */
105 size_t left = 2 * i;
106 size_t right = 2 * i + 1;
107 size_t max = i;
108 if (left <= cnt && do_compare (array, left, max, size, compare, aux) > 0)
109 max = left;
110 if (right <= cnt
111 && do_compare (array, right, max, size, compare, aux) > 0)
112 max = right;
113
114 /* If the maximum value is already in element I, we're
115 done. */
116 if (max == i)
117 break;
118
119 /* Swap and continue down the heap. */
120 do_swap (array, i, max, size);
121 i = max;
122 }
123}
124
125/* Sorts ARRAY, which contains CNT elements of SIZE bytes each,
126 using COMPARE to compare elements, passing AUX as auxiliary
127 data. When COMPARE is passed a pair of elements A and B,
128 respectively, it must return a strcmp()-type result, i.e. less
129 than zero if A < B, zero if A == B, greater than zero if A >
130 B. Runs in O(n lg n) time and O(1) space in CNT. */
131void
132sort (void *array, size_t cnt, size_t size,
133 int (*compare) (const void *, const void *, void *aux),
134 void *aux)
135{
136 size_t i;
137
138 ASSERT (array != NULL || cnt == 0);
139 ASSERT (compare != NULL);
140 ASSERT (size > 0);
141
142 /* Build a heap. */
143 for (i = cnt / 2; i > 0; i--)
144 heapify (array, i, cnt, size, compare, aux);
145
146 /* Sort the heap. */
147 for (i = cnt; i > 1; i--)
148 {
149 do_swap (array, 1, i, size);
150 heapify (array, 1, i - 1, size, compare, aux);
151 }
152}
153
154/* Searches ARRAY, which contains CNT elements of SIZE bytes
155 each, for the given KEY. Returns a match is found, otherwise
156 a null pointer. If there are multiple matches, returns an
157 arbitrary one of them.
158
159 ARRAY must be sorted in order according to COMPARE.
160
161 Uses COMPARE to compare elements. When COMPARE is passed a
162 pair of elements A and B, respectively, it must return a
163 strcmp()-type result, i.e. less than zero if A < B, zero if A
164 == B, greater than zero if A > B. */
165void *
166bsearch (const void *key, const void *array, size_t cnt,
167 size_t size, int (*compare) (const void *, const void *))
168{
169 return binary_search (key, array, cnt, size, compare_thunk, &compare);
170}
171
172/* Searches ARRAY, which contains CNT elements of SIZE bytes
173 each, for the given KEY. Returns a match is found, otherwise
174 a null pointer. If there are multiple matches, returns an
175 arbitrary one of them.
176
177 ARRAY must be sorted in order according to COMPARE.
178
179 Uses COMPARE to compare elements, passing AUX as auxiliary
180 data. When COMPARE is passed a pair of elements A and B,
181 respectively, it must return a strcmp()-type result, i.e. less
182 than zero if A < B, zero if A == B, greater than zero if A >
183 B. */
184void *
185binary_search (const void *key, const void *array, size_t cnt, size_t size,
186 int (*compare) (const void *, const void *, void *aux),
187 void *aux)
188{
189 const unsigned char *first = array;
190 const unsigned char *last = array + size * cnt;
191
192 while (first < last)
193 {
194 size_t range = (last - first) / size;
195 const unsigned char *middle = first + (range / 2) * size;
196 int cmp = compare (key, middle, aux);
197
198 if (cmp < 0)
199 last = middle;
200 else if (cmp > 0)
201 first = middle + size;
202 else
203 return (void *) middle;
204 }
205
206 return NULL;
207}
208
diff --git a/pintos-progos/lib/stdlib.h b/pintos-progos/lib/stdlib.h
new file mode 100644
index 0000000..d14afa3
--- /dev/null
+++ b/pintos-progos/lib/stdlib.h
@@ -0,0 +1,22 @@
1#ifndef __LIB_STDLIB_H
2#define __LIB_STDLIB_H
3
4#include <stddef.h>
5
6/* Standard functions. */
7int atoi (const char *);
8void qsort (void *array, size_t cnt, size_t size,
9 int (*compare) (const void *, const void *));
10void *bsearch (const void *key, const void *array, size_t cnt,
11 size_t size, int (*compare) (const void *, const void *));
12
13/* Nonstandard functions. */
14void sort (void *array, size_t cnt, size_t size,
15 int (*compare) (const void *, const void *, void *aux),
16 void *aux);
17void *binary_search (const void *key, const void *array, size_t cnt,
18 size_t size,
19 int (*compare) (const void *, const void *, void *aux),
20 void *aux);
21
22#endif /* lib/stdlib.h */
diff --git a/pintos-progos/lib/string.c b/pintos-progos/lib/string.c
new file mode 100644
index 0000000..d223c89
--- /dev/null
+++ b/pintos-progos/lib/string.c
@@ -0,0 +1,375 @@
1#include <string.h>
2#include <debug.h>
3
4/* Copies SIZE bytes from SRC to DST, which must not overlap.
5 Returns DST. */
6void *
7memcpy (void *dst_, const void *src_, size_t size)
8{
9 unsigned char *dst = dst_;
10 const unsigned char *src = src_;
11
12 ASSERT (dst != NULL || size == 0);
13 ASSERT (src != NULL || size == 0);
14
15 while (size-- > 0)
16 *dst++ = *src++;
17
18 return dst_;
19}
20
21/* Copies SIZE bytes from SRC to DST, which are allowed to
22 overlap. Returns DST. */
23void *
24memmove (void *dst_, const void *src_, size_t size)
25{
26 unsigned char *dst = dst_;
27 const unsigned char *src = src_;
28
29 ASSERT (dst != NULL || size == 0);
30 ASSERT (src != NULL || size == 0);
31
32 if (dst < src)
33 {
34 while (size-- > 0)
35 *dst++ = *src++;
36 }
37 else
38 {
39 dst += size;
40 src += size;
41 while (size-- > 0)
42 *--dst = *--src;
43 }
44
45 return dst;
46}
47
48/* Find the first differing byte in the two blocks of SIZE bytes
49 at A and B. Returns a positive value if the byte in A is
50 greater, a negative value if the byte in B is greater, or zero
51 if blocks A and B are equal. */
52int
53memcmp (const void *a_, const void *b_, size_t size)
54{
55 const unsigned char *a = a_;
56 const unsigned char *b = b_;
57
58 ASSERT (a != NULL || size == 0);
59 ASSERT (b != NULL || size == 0);
60
61 for (; size-- > 0; a++, b++)
62 if (*a != *b)
63 return *a > *b ? +1 : -1;
64 return 0;
65}
66
67/* Finds the first differing characters in strings A and B.
68 Returns a positive value if the character in A (as an unsigned
69 char) is greater, a negative value if the character in B (as
70 an unsigned char) is greater, or zero if strings A and B are
71 equal. */
72int
73strcmp (const char *a_, const char *b_)
74{
75 const unsigned char *a = (const unsigned char *) a_;
76 const unsigned char *b = (const unsigned char *) b_;
77
78 ASSERT (a != NULL);
79 ASSERT (b != NULL);
80
81 while (*a != '\0' && *a == *b)
82 {
83 a++;
84 b++;
85 }
86
87 return *a < *b ? -1 : *a > *b;
88}
89
90/* Returns a pointer to the first occurrence of CH in the first
91 SIZE bytes starting at BLOCK. Returns a null pointer if CH
92 does not occur in BLOCK. */
93void *
94memchr (const void *block_, int ch_, size_t size)
95{
96 const unsigned char *block = block_;
97 unsigned char ch = ch_;
98
99 ASSERT (block != NULL || size == 0);
100
101 for (; size-- > 0; block++)
102 if (*block == ch)
103 return (void *) block;
104
105 return NULL;
106}
107
108/* Finds and returns the first occurrence of C in STRING, or a
109 null pointer if C does not appear in STRING. If C == '\0'
110 then returns a pointer to the null terminator at the end of
111 STRING. */
112char *
113strchr (const char *string, int c_)
114{
115 char c = c_;
116
117 ASSERT (string != NULL);
118
119 for (;;)
120 if (*string == c)
121 return (char *) string;
122 else if (*string == '\0')
123 return NULL;
124 else
125 string++;
126}
127
128/* Returns the length of the initial substring of STRING that
129 consists of characters that are not in STOP. */
130size_t
131strcspn (const char *string, const char *stop)
132{
133 size_t length;
134
135 for (length = 0; string[length] != '\0'; length++)
136 if (strchr (stop, string[length]) != NULL)
137 break;
138 return length;
139}
140
141/* Returns a pointer to the first character in STRING that is
142 also in STOP. If no character in STRING is in STOP, returns a
143 null pointer. */
144char *
145strpbrk (const char *string, const char *stop)
146{
147 for (; *string != '\0'; string++)
148 if (strchr (stop, *string) != NULL)
149 return (char *) string;
150 return NULL;
151}
152
153/* Returns a pointer to the last occurrence of C in STRING.
154 Returns a null pointer if C does not occur in STRING. */
155char *
156strrchr (const char *string, int c_)
157{
158 char c = c_;
159 const char *p = NULL;
160
161 for (; *string != '\0'; string++)
162 if (*string == c)
163 p = string;
164 return (char *) p;
165}
166
167/* Returns the length of the initial substring of STRING that
168 consists of characters in SKIP. */
169size_t
170strspn (const char *string, const char *skip)
171{
172 size_t length;
173
174 for (length = 0; string[length] != '\0'; length++)
175 if (strchr (skip, string[length]) == NULL)
176 break;
177 return length;
178}
179
180/* Returns a pointer to the first occurrence of NEEDLE within
181 HAYSTACK. Returns a null pointer if NEEDLE does not exist
182 within HAYSTACK. */
183char *
184strstr (const char *haystack, const char *needle)
185{
186 size_t haystack_len = strlen (haystack);
187 size_t needle_len = strlen (needle);
188
189 if (haystack_len >= needle_len)
190 {
191 size_t i;
192
193 for (i = 0; i <= haystack_len - needle_len; i++)
194 if (!memcmp (haystack + i, needle, needle_len))
195 return (char *) haystack + i;
196 }
197
198 return NULL;
199}
200
201/* Breaks a string into tokens separated by DELIMITERS. The
202 first time this function is called, S should be the string to
203 tokenize, and in subsequent calls it must be a null pointer.
204 SAVE_PTR is the address of a `char *' variable used to keep
205 track of the tokenizer's position. The return value each time
206 is the next token in the string, or a null pointer if no
207 tokens remain.
208
209 This function treats multiple adjacent delimiters as a single
210 delimiter. The returned tokens will never be length 0.
211 DELIMITERS may change from one call to the next within a
212 single string.
213
214 strtok_r() modifies the string S, changing delimiters to null
215 bytes. Thus, S must be a modifiable string. String literals,
216 in particular, are *not* modifiable in C, even though for
217 backward compatibility they are not `const'.
218
219 Example usage:
220
221 char s[] = " String to tokenize. ";
222 char *token, *save_ptr;
223
224 for (token = strtok_r (s, " ", &save_ptr); token != NULL;
225 token = strtok_r (NULL, " ", &save_ptr))
226 printf ("'%s'\n", token);
227
228 outputs:
229
230 'String'
231 'to'
232 'tokenize.'
233*/
234char *
235strtok_r (char *s, const char *delimiters, char **save_ptr)
236{
237 char *token;
238
239 ASSERT (delimiters != NULL);
240 ASSERT (save_ptr != NULL);
241
242 /* If S is nonnull, start from it.
243 If S is null, start from saved position. */
244 if (s == NULL)
245 s = *save_ptr;
246 ASSERT (s != NULL);
247
248 /* Skip any DELIMITERS at our current position. */
249 while (strchr (delimiters, *s) != NULL)
250 {
251 /* strchr() will always return nonnull if we're searching
252 for a null byte, because every string contains a null
253 byte (at the end). */
254 if (*s == '\0')
255 {
256 *save_ptr = s;
257 return NULL;
258 }
259
260 s++;
261 }
262
263 /* Skip any non-DELIMITERS up to the end of the string. */
264 token = s;
265 while (strchr (delimiters, *s) == NULL)
266 s++;
267 if (*s != '\0')
268 {
269 *s = '\0';
270 *save_ptr = s + 1;
271 }
272 else
273 *save_ptr = s;
274 return token;
275}
276
277/* Sets the SIZE bytes in DST to VALUE. */
278void *
279memset (void *dst_, int value, size_t size)
280{
281 unsigned char *dst = dst_;
282
283 ASSERT (dst != NULL || size == 0);
284
285 while (size-- > 0)
286 *dst++ = value;
287
288 return dst_;
289}
290
291/* Returns the length of STRING. */
292size_t
293strlen (const char *string)
294{
295 const char *p;
296
297 ASSERT (string != NULL);
298
299 for (p = string; *p != '\0'; p++)
300 continue;
301 return p - string;
302}
303
304/* If STRING is less than MAXLEN characters in length, returns
305 its actual length. Otherwise, returns MAXLEN. */
306size_t
307strnlen (const char *string, size_t maxlen)
308{
309 size_t length;
310
311 for (length = 0; string[length] != '\0' && length < maxlen; length++)
312 continue;
313 return length;
314}
315
316/* Copies string SRC to DST. If SRC is longer than SIZE - 1
317 characters, only SIZE - 1 characters are copied. A null
318 terminator is always written to DST, unless SIZE is 0.
319 Returns the length of SRC, not including the null terminator.
320
321 strlcpy() is not in the standard C library, but it is an
322 increasingly popular extension. See
323 http://www.courtesan.com/todd/papers/strlcpy.html for
324 information on strlcpy(). */
325size_t
326strlcpy (char *dst, const char *src, size_t size)
327{
328 size_t src_len;
329
330 ASSERT (dst != NULL);
331 ASSERT (src != NULL);
332
333 src_len = strlen (src);
334 if (size > 0)
335 {
336 size_t dst_len = size - 1;
337 if (src_len < dst_len)
338 dst_len = src_len;
339 memcpy (dst, src, dst_len);
340 dst[dst_len] = '\0';
341 }
342 return src_len;
343}
344
345/* Concatenates string SRC to DST. The concatenated string is
346 limited to SIZE - 1 characters. A null terminator is always
347 written to DST, unless SIZE is 0. Returns the length that the
348 concatenated string would have assuming that there was
349 sufficient space, not including a null terminator.
350
351 strlcat() is not in the standard C library, but it is an
352 increasingly popular extension. See
353 http://www.courtesan.com/todd/papers/strlcpy.html for
354 information on strlcpy(). */
355size_t
356strlcat (char *dst, const char *src, size_t size)
357{
358 size_t src_len, dst_len;
359
360 ASSERT (dst != NULL);
361 ASSERT (src != NULL);
362
363 src_len = strlen (src);
364 dst_len = strlen (dst);
365 if (size > 0 && dst_len < size)
366 {
367 size_t copy_cnt = size - dst_len - 1;
368 if (src_len < copy_cnt)
369 copy_cnt = src_len;
370 memcpy (dst + dst_len, src, copy_cnt);
371 dst[dst_len + copy_cnt] = '\0';
372 }
373 return src_len + dst_len;
374}
375
diff --git a/pintos-progos/lib/string.h b/pintos-progos/lib/string.h
new file mode 100644
index 0000000..1fff82a
--- /dev/null
+++ b/pintos-progos/lib/string.h
@@ -0,0 +1,35 @@
1#ifndef __LIB_STRING_H
2#define __LIB_STRING_H
3
4#include <stddef.h>
5
6/* Standard. */
7void *memcpy (void *, const void *, size_t);
8void *memmove (void *, const void *, size_t);
9char *strncat (char *, const char *, size_t);
10int memcmp (const void *, const void *, size_t);
11int strcmp (const char *, const char *);
12void *memchr (const void *, int, size_t);
13char *strchr (const char *, int);
14size_t strcspn (const char *, const char *);
15char *strpbrk (const char *, const char *);
16char *strrchr (const char *, int);
17size_t strspn (const char *, const char *);
18char *strstr (const char *, const char *);
19void *memset (void *, int, size_t);
20size_t strlen (const char *);
21
22/* Extensions. */
23size_t strlcpy (char *, const char *, size_t);
24size_t strlcat (char *, const char *, size_t);
25char *strtok_r (char *, const char *, char **);
26size_t strnlen (const char *, size_t);
27
28/* Try to be helpful. */
29#define strcpy dont_use_strcpy_use_strlcpy
30#define strncpy dont_use_strncpy_use_strlcpy
31#define strcat dont_use_strcat_use_strlcat
32#define strncat dont_use_strncat_use_strlcat
33#define strtok dont_use_strtok_use_strtok_r
34
35#endif /* lib/string.h */
diff --git a/pintos-progos/lib/syscall-nr.h b/pintos-progos/lib/syscall-nr.h
new file mode 100644
index 0000000..21a7af9
--- /dev/null
+++ b/pintos-progos/lib/syscall-nr.h
@@ -0,0 +1,34 @@
1#ifndef __LIB_SYSCALL_NR_H
2#define __LIB_SYSCALL_NR_H
3
4/* System call numbers. */
5enum
6 {
7 /* Projects 2 and later. */
8 SYS_HALT, /* Halt the operating system. */
9 SYS_EXIT, /* Terminate this process. */
10 SYS_EXEC, /* Start another process. */
11 SYS_WAIT, /* Wait for a child process to die. */
12 SYS_CREATE, /* Create a file. */
13 SYS_REMOVE, /* Delete a file. */
14 SYS_OPEN, /* Open a file. */
15 SYS_FILESIZE, /* Obtain a file's size. */
16 SYS_READ, /* Read from a file. */
17 SYS_WRITE, /* Write to a file. */
18 SYS_SEEK, /* Change position in a file. */
19 SYS_TELL, /* Report current position in a file. */
20 SYS_CLOSE, /* Close a file. */
21
22 /* Project 3 and optionally project 4. */
23 SYS_MMAP, /* Map a file into memory. */
24 SYS_MUNMAP, /* Remove a memory mapping. */
25
26 /* Project 4 only. */
27 SYS_CHDIR, /* Change the current directory. */
28 SYS_MKDIR, /* Create a directory. */
29 SYS_READDIR, /* Reads a directory entry. */
30 SYS_ISDIR, /* Tests if a fd represents a directory. */
31 SYS_INUMBER /* Returns the inode number for a fd. */
32 };
33
34#endif /* lib/syscall-nr.h */
diff --git a/pintos-progos/lib/user/console.c b/pintos-progos/lib/user/console.c
new file mode 100644
index 0000000..22bdc8c
--- /dev/null
+++ b/pintos-progos/lib/user/console.c
@@ -0,0 +1,94 @@
1#include <stdio.h>
2#include <string.h>
3#include <syscall.h>
4#include <syscall-nr.h>
5
6/* The standard vprintf() function,
7 which is like printf() but uses a va_list. */
8int
9vprintf (const char *format, va_list args)
10{
11 return vhprintf (STDOUT_FILENO, format, args);
12}
13
14/* Like printf(), but writes output to the given HANDLE. */
15int
16hprintf (int handle, const char *format, ...)
17{
18 va_list args;
19 int retval;
20
21 va_start (args, format);
22 retval = vhprintf (handle, format, args);
23 va_end (args);
24
25 return retval;
26}
27
28/* Writes string S to the console, followed by a new-line
29 character. */
30int
31puts (const char *s)
32{
33 write (STDOUT_FILENO, s, strlen (s));
34 putchar ('\n');
35
36 return 0;
37}
38
39/* Writes C to the console. */
40int
41putchar (int c)
42{
43 char c2 = c;
44 write (STDOUT_FILENO, &c2, 1);
45 return c;
46}
47
48/* Auxiliary data for vhprintf_helper(). */
49struct vhprintf_aux
50 {
51 char buf[64]; /* Character buffer. */
52 char *p; /* Current position in buffer. */
53 int char_cnt; /* Total characters written so far. */
54 int handle; /* Output file handle. */
55 };
56
57static void add_char (char, void *);
58static void flush (struct vhprintf_aux *);
59
60/* Formats the printf() format specification FORMAT with
61 arguments given in ARGS and writes the output to the given
62 HANDLE. */
63int
64vhprintf (int handle, const char *format, va_list args)
65{
66 struct vhprintf_aux aux;
67 aux.p = aux.buf;
68 aux.char_cnt = 0;
69 aux.handle = handle;
70 __vprintf (format, args, add_char, &aux);
71 flush (&aux);
72 return aux.char_cnt;
73}
74
75/* Adds C to the buffer in AUX, flushing it if the buffer fills
76 up. */
77static void
78add_char (char c, void *aux_)
79{
80 struct vhprintf_aux *aux = aux_;
81 *aux->p++ = c;
82 if (aux->p >= aux->buf + sizeof aux->buf)
83 flush (aux);
84 aux->char_cnt++;
85}
86
87/* Flushes the buffer in AUX. */
88static void
89flush (struct vhprintf_aux *aux)
90{
91 if (aux->p > aux->buf)
92 write (aux->handle, aux->buf, aux->p - aux->buf);
93 aux->p = aux->buf;
94}
diff --git a/pintos-progos/lib/user/debug.c b/pintos-progos/lib/user/debug.c
new file mode 100644
index 0000000..f49b874
--- /dev/null
+++ b/pintos-progos/lib/user/debug.c
@@ -0,0 +1,25 @@
1#include <debug.h>
2#include <stdarg.h>
3#include <stdbool.h>
4#include <stdio.h>
5#include <syscall.h>
6
7/* Aborts the user program, printing the source file name, line
8 number, and function name, plus a user-specific message. */
9void
10debug_panic (const char *file, int line, const char *function,
11 const char *message, ...)
12{
13 va_list args;
14
15 printf ("User process ABORT at %s:%d in %s(): ", file, line, function);
16
17 va_start (args, message);
18 vprintf (message, args);
19 printf ("\n");
20 va_end (args);
21
22 debug_backtrace ();
23
24 exit (1);
25}
diff --git a/pintos-progos/lib/user/entry.c b/pintos-progos/lib/user/entry.c
new file mode 100644
index 0000000..a707c70
--- /dev/null
+++ b/pintos-progos/lib/user/entry.c
@@ -0,0 +1,10 @@
1#include <syscall.h>
2
3int main (int, char *[]);
4void _start (int argc, char *argv[]);
5
6void
7_start (int argc, char *argv[])
8{
9 exit (main (argc, argv));
10}
diff --git a/pintos-progos/lib/user/stdio.h b/pintos-progos/lib/user/stdio.h
new file mode 100644
index 0000000..b9f3cc6
--- /dev/null
+++ b/pintos-progos/lib/user/stdio.h
@@ -0,0 +1,7 @@
1#ifndef __LIB_USER_STDIO_H
2#define __LIB_USER_STDIO_H
3
4int hprintf (int, const char *, ...) PRINTF_FORMAT (2, 3);
5int vhprintf (int, const char *, va_list) PRINTF_FORMAT (2, 0);
6
7#endif /* lib/user/stdio.h */
diff --git a/pintos-progos/lib/user/syscall.c b/pintos-progos/lib/user/syscall.c
new file mode 100644
index 0000000..0a467f8
--- /dev/null
+++ b/pintos-progos/lib/user/syscall.c
@@ -0,0 +1,184 @@
1#include <syscall.h>
2#include "../syscall-nr.h"
3
4/* Invokes syscall NUMBER, passing no arguments, and returns the
5 return value as an `int'. */
6#define syscall0(NUMBER) \
7 ({ \
8 int retval; \
9 asm volatile \
10 ("pushl %[number]; int $0x30; addl $4, %%esp" \
11 : "=a" (retval) \
12 : [number] "i" (NUMBER) \
13 : "memory"); \
14 retval; \
15 })
16
17/* Invokes syscall NUMBER, passing argument ARG0, and returns the
18 return value as an `int'. */
19#define syscall1(NUMBER, ARG0) \
20 ({ \
21 int retval; \
22 asm volatile \
23 ("pushl %[arg0]; pushl %[number]; int $0x30; addl $8, %%esp" \
24 : "=a" (retval) \
25 : [number] "i" (NUMBER), \
26 [arg0] "g" (ARG0) \
27 : "memory"); \
28 retval; \
29 })
30
31/* Invokes syscall NUMBER, passing arguments ARG0 and ARG1, and
32 returns the return value as an `int'. */
33#define syscall2(NUMBER, ARG0, ARG1) \
34 ({ \
35 int retval; \
36 asm volatile \
37 ("pushl %[arg1]; pushl %[arg0]; " \
38 "pushl %[number]; int $0x30; addl $12, %%esp" \
39 : "=a" (retval) \
40 : [number] "i" (NUMBER), \
41 [arg0] "r" (ARG0), \
42 [arg1] "r" (ARG1) \
43 : "memory"); \
44 retval; \
45 })
46
47/* Invokes syscall NUMBER, passing arguments ARG0, ARG1, and
48 ARG2, and returns the return value as an `int'. */
49#define syscall3(NUMBER, ARG0, ARG1, ARG2) \
50 ({ \
51 int retval; \
52 asm volatile \
53 ("pushl %[arg2]; pushl %[arg1]; pushl %[arg0]; " \
54 "pushl %[number]; int $0x30; addl $16, %%esp" \
55 : "=a" (retval) \
56 : [number] "i" (NUMBER), \
57 [arg0] "r" (ARG0), \
58 [arg1] "r" (ARG1), \
59 [arg2] "r" (ARG2) \
60 : "memory"); \
61 retval; \
62 })
63
64void
65halt (void)
66{
67 syscall0 (SYS_HALT);
68 NOT_REACHED ();
69}
70
71void
72exit (int status)
73{
74 syscall1 (SYS_EXIT, status);
75 NOT_REACHED ();
76}
77
78pid_t
79exec (const char *cmd_line)
80{
81 return (pid_t) syscall1 (SYS_EXEC, cmd_line);
82}
83
84int
85wait (pid_t pid)
86{
87 return syscall1 (SYS_WAIT, pid);
88}
89
90bool
91create (const char *file, unsigned initial_size)
92{
93 return syscall2 (SYS_CREATE, file, initial_size);
94}
95
96bool
97remove (const char *file)
98{
99 return syscall1 (SYS_REMOVE, file);
100}
101
102int
103open (const char *file)
104{
105 return syscall1 (SYS_OPEN, file);
106}
107
108int
109filesize (int fd)
110{
111 return syscall1 (SYS_FILESIZE, fd);
112}
113
114int
115read (int fd, void *buffer, unsigned size)
116{
117 return syscall3 (SYS_READ, fd, buffer, size);
118}
119
120int
121write (int fd, const void *buffer, unsigned size)
122{
123 return syscall3 (SYS_WRITE, fd, buffer, size);
124}
125
126void
127seek (int fd, unsigned position)
128{
129 syscall2 (SYS_SEEK, fd, position);
130}
131
132unsigned
133tell (int fd)
134{
135 return syscall1 (SYS_TELL, fd);
136}
137
138void
139close (int fd)
140{
141 syscall1 (SYS_CLOSE, fd);
142}
143
144mapid_t
145mmap (int fd, void *addr)
146{
147 return syscall2 (SYS_MMAP, fd, addr);
148}
149
150void
151munmap (mapid_t mapid)
152{
153 syscall1 (SYS_MUNMAP, mapid);
154}
155
156bool
157chdir (const char *dir)
158{
159 return syscall1 (SYS_CHDIR, dir);
160}
161
162bool
163mkdir (const char *dir)
164{
165 return syscall1 (SYS_MKDIR, dir);
166}
167
168bool
169readdir (int fd, char name[READDIR_MAX_LEN + 1])
170{
171 return syscall2 (SYS_READDIR, fd, name);
172}
173
174bool
175isdir (int fd)
176{
177 return syscall1 (SYS_ISDIR, fd);
178}
179
180int
181inumber (int fd)
182{
183 return syscall1 (SYS_INUMBER, fd);
184}
diff --git a/pintos-progos/lib/user/syscall.h b/pintos-progos/lib/user/syscall.h
new file mode 100644
index 0000000..a1bcf0b
--- /dev/null
+++ b/pintos-progos/lib/user/syscall.h
@@ -0,0 +1,48 @@
1#ifndef __LIB_USER_SYSCALL_H
2#define __LIB_USER_SYSCALL_H
3
4#include <stdbool.h>
5#include <debug.h>
6
7/* Process identifier. */
8typedef int pid_t;
9#define PID_ERROR ((pid_t) -1)
10
11/* Map region identifier. */
12typedef int mapid_t;
13#define MAP_FAILED ((mapid_t) -1)
14
15/* Maximum characters in a filename written by readdir(). */
16#define READDIR_MAX_LEN 14
17
18/* Typical return values from main() and arguments to exit(). */
19#define EXIT_SUCCESS 0 /* Successful execution. */
20#define EXIT_FAILURE 1 /* Unsuccessful execution. */
21
22/* Projects 2 and later. */
23void halt (void) NO_RETURN;
24void exit (int status) NO_RETURN;
25pid_t exec (const char *cmd_line);
26int wait (pid_t);
27bool create (const char *file, unsigned initial_size);
28bool remove (const char *file);
29int open (const char *file);
30int filesize (int fd);
31int read (int fd, void *buffer, unsigned length);
32int write (int fd, const void *buffer, unsigned length);
33void seek (int fd, unsigned position);
34unsigned tell (int fd);
35void close (int fd);
36
37/* Project 3 and optionally project 4. */
38mapid_t mmap (int fd, void *addr);
39void munmap (mapid_t);
40
41/* Project 4 only. */
42bool chdir (const char *dir);
43bool mkdir (const char *dir);
44bool readdir (int fd, char name[READDIR_MAX_LEN + 1]);
45bool isdir (int fd);
46int inumber (int fd);
47
48#endif /* lib/user/syscall.h */
diff --git a/pintos-progos/lib/user/user.lds b/pintos-progos/lib/user/user.lds
new file mode 100644
index 0000000..cc6d6c0
--- /dev/null
+++ b/pintos-progos/lib/user/user.lds
@@ -0,0 +1,57 @@
1OUTPUT_FORMAT("elf32-i386")
2OUTPUT_ARCH(i386)
3ENTRY(_start)
4
5SECTIONS
6{
7 /* Read-only sections, merged into text segment: */
8 __executable_start = 0x08048000 + SIZEOF_HEADERS;
9 . = 0x08048000 + SIZEOF_HEADERS;
10 .text : { *(.text) } = 0x90
11 .rodata : { *(.rodata) }
12
13 /* Adjust the address for the data segment. We want to adjust up to
14 the same address within the page on the next page up. */
15 . = ALIGN (0x1000) - ((0x1000 - .) & (0x1000 - 1));
16 . = DATA_SEGMENT_ALIGN (0x1000, 0x1000);
17
18 .data : { *(.data) }
19 .bss : { *(.bss) }
20
21 /* Stabs debugging sections. */
22 .stab 0 : { *(.stab) }
23 .stabstr 0 : { *(.stabstr) }
24 .stab.excl 0 : { *(.stab.excl) }
25 .stab.exclstr 0 : { *(.stab.exclstr) }
26 .stab.index 0 : { *(.stab.index) }
27 .stab.indexstr 0 : { *(.stab.indexstr) }
28 .comment 0 : { *(.comment) }
29
30 /* DWARF debug sections.
31 Symbols in the DWARF debugging sections are relative to the beginning
32 of the section so we begin them at 0. */
33 /* DWARF 1 */
34 .debug 0 : { *(.debug) }
35 .line 0 : { *(.line) }
36 /* GNU DWARF 1 extensions */
37 .debug_srcinfo 0 : { *(.debug_srcinfo) }
38 .debug_sfnames 0 : { *(.debug_sfnames) }
39 /* DWARF 1.1 and DWARF 2 */
40 .debug_aranges 0 : { *(.debug_aranges) }
41 .debug_pubnames 0 : { *(.debug_pubnames) }
42 /* DWARF 2 */
43 .debug_info 0 : { *(.debug_info .gnu.linkonce.wi.*) }
44 .debug_abbrev 0 : { *(.debug_abbrev) }
45 .debug_line 0 : { *(.debug_line) }
46 .debug_frame 0 : { *(.debug_frame) }
47 .debug_str 0 : { *(.debug_str) }
48 .debug_loc 0 : { *(.debug_loc) }
49 .debug_macinfo 0 : { *(.debug_macinfo) }
50 /* SGI/MIPS DWARF 2 extensions */
51 .debug_weaknames 0 : { *(.debug_weaknames) }
52 .debug_funcnames 0 : { *(.debug_funcnames) }
53 .debug_typenames 0 : { *(.debug_typenames) }
54 .debug_varnames 0 : { *(.debug_varnames) }
55 /DISCARD/ : { *(.note.GNU-stack) }
56 /DISCARD/ : { *(.eh_frame) }
57}
diff --git a/pintos-progos/lib/ustar.c b/pintos-progos/lib/ustar.c
new file mode 100644
index 0000000..49af69a
--- /dev/null
+++ b/pintos-progos/lib/ustar.c
@@ -0,0 +1,228 @@
1#include <ustar.h>
2#include <limits.h>
3#include <packed.h>
4#include <stdio.h>
5#include <string.h>
6
7/* Header for ustar-format tar archive. See the documentation of
8 the "pax" utility in [SUSv3] for the the "ustar" format
9 specification. */
10struct ustar_header
11 {
12 char name[100]; /* File name. Null-terminated if room. */
13 char mode[8]; /* Permissions as octal string. */
14 char uid[8]; /* User ID as octal string. */
15 char gid[8]; /* Group ID as octal string. */
16 char size[12]; /* File size in bytes as octal string. */
17 char mtime[12]; /* Modification time in seconds
18 from Jan 1, 1970, as octal string. */
19 char chksum[8]; /* Sum of octets in header as octal string. */
20 char typeflag; /* An enum ustar_type value. */
21 char linkname[100]; /* Name of link target.
22 Null-terminated if room. */
23 char magic[6]; /* "ustar\0" */
24 char version[2]; /* "00" */
25 char uname[32]; /* User name, always null-terminated. */
26 char gname[32]; /* Group name, always null-terminated. */
27 char devmajor[8]; /* Device major number as octal string. */
28 char devminor[8]; /* Device minor number as octal string. */
29 char prefix[155]; /* Prefix to file name.
30 Null-terminated if room. */
31 char padding[12]; /* Pad to 512 bytes. */
32 }
33PACKED;
34
35/* Returns the checksum for the given ustar format HEADER. */
36static unsigned int
37calculate_chksum (const struct ustar_header *h)
38{
39 const uint8_t *header = (const uint8_t *) h;
40 unsigned int chksum;
41 size_t i;
42
43 chksum = 0;
44 for (i = 0; i < USTAR_HEADER_SIZE; i++)
45 {
46 /* The ustar checksum is calculated as if the chksum field
47 were all spaces. */
48 const size_t chksum_start = offsetof (struct ustar_header, chksum);
49 const size_t chksum_end = chksum_start + sizeof h->chksum;
50 bool in_chksum_field = i >= chksum_start && i < chksum_end;
51 chksum += in_chksum_field ? ' ' : header[i];
52 }
53 return chksum;
54}
55
56/* Drop possibly dangerous prefixes from FILE_NAME and return the
57 stripped name. An archive with file names that start with "/"
58 or "../" could cause a naive tar extractor to write to
59 arbitrary parts of the file system, not just the destination
60 directory. We don't want to create such archives or be such a
61 naive extractor.
62
63 The return value can be a suffix of FILE_NAME or a string
64 literal. */
65static const char *
66strip_antisocial_prefixes (const char *file_name)
67{
68 while (*file_name == '/'
69 || !memcmp (file_name, "./", 2)
70 || !memcmp (file_name, "../", 3))
71 file_name = strchr (file_name, '/') + 1;
72 return *file_name == '\0' || !strcmp (file_name, "..") ? "." : file_name;
73}
74
75/* Composes HEADER as a USTAR_HEADER_SIZE (512)-byte archive
76 header in ustar format for a SIZE-byte file named FILE_NAME of
77 the given TYPE. The caller is responsible for writing the
78 header to a file or device.
79
80 If successful, returns true. On failure (due to an
81 excessively long file name), returns false. */
82bool
83ustar_make_header (const char *file_name, enum ustar_type type,
84 int size, char header[USTAR_HEADER_SIZE])
85{
86 struct ustar_header *h = (struct ustar_header *) header;
87
88 ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE);
89 ASSERT (type == USTAR_REGULAR || type == USTAR_DIRECTORY);
90
91 /* Check file name. */
92 file_name = strip_antisocial_prefixes (file_name);
93 if (strlen (file_name) > 99)
94 {
95 printf ("%s: file name too long\n", file_name);
96 return false;
97 }
98
99 /* Fill in header except for final checksum. */
100 memset (h, 0, sizeof *h);
101 strlcpy (h->name, file_name, sizeof h->name);
102 snprintf (h->mode, sizeof h->mode, "%07o",
103 type == USTAR_REGULAR ? 0644 : 0755);
104 strlcpy (h->uid, "0000000", sizeof h->uid);
105 strlcpy (h->gid, "0000000", sizeof h->gid);
106 snprintf (h->size, sizeof h->size, "%011o", size);
107 snprintf (h->mtime, sizeof h->size, "%011o", 1136102400);
108 h->typeflag = type;
109 strlcpy (h->magic, "ustar", sizeof h->magic);
110 h->version[0] = h->version[1] = '0';
111 strlcpy (h->gname, "root", sizeof h->gname);
112 strlcpy (h->uname, "root", sizeof h->uname);
113
114 /* Compute and fill in final checksum. */
115 snprintf (h->chksum, sizeof h->chksum, "%07o", calculate_chksum (h));
116
117 return true;
118}
119
120/* Parses a SIZE-byte octal field in S in the format used by
121 ustar format. If successful, stores the field's value in
122 *VALUE and returns true; on failure, returns false.
123
124 ustar octal fields consist of a sequence of octal digits
125 terminated by a space or a null byte. The ustar specification
126 seems ambiguous as to whether these fields must be padded on
127 the left with '0's, so we accept any field that fits in the
128 available space, regardless of whether it fills the space. */
129static bool
130parse_octal_field (const char *s, size_t size, unsigned long int *value)
131{
132 size_t ofs;
133
134 *value = 0;
135 for (ofs = 0; ofs < size; ofs++)
136 {
137 char c = s[ofs];
138 if (c >= '0' && c <= '7')
139 {
140 if (*value > ULONG_MAX / 8)
141 {
142 /* Overflow. */
143 return false;
144 }
145 *value = c - '0' + *value * 8;
146 }
147 else if (c == ' ' || c == '\0')
148 {
149 /* End of field, but disallow completely empty
150 fields. */
151 return ofs > 0;
152 }
153 else
154 {
155 /* Bad character. */
156 return false;
157 }
158 }
159
160 /* Field did not end in space or null byte. */
161 return false;
162}
163
164/* Returns true if the CNT bytes starting at BLOCK are all zero,
165 false otherwise. */
166static bool
167is_all_zeros (const char *block, size_t cnt)
168{
169 while (cnt-- > 0)
170 if (*block++ != 0)
171 return false;
172 return true;
173}
174
175/* Parses HEADER as a ustar-format archive header for a regular
176 file or directory. If successful, stores the archived file's
177 name in *FILE_NAME (as a pointer into HEADER or a string
178 literal), its type in *TYPE, and its size in bytes in *SIZE,
179 and returns a null pointer. On failure, returns a
180 human-readable error message. */
181const char *
182ustar_parse_header (const char header[USTAR_HEADER_SIZE],
183 const char **file_name, enum ustar_type *type, int *size)
184{
185 const struct ustar_header *h = (const struct ustar_header *) header;
186 unsigned long int chksum, size_ul;
187
188 ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE);
189
190 /* Detect end of archive. */
191 if (is_all_zeros (header, USTAR_HEADER_SIZE))
192 {
193 *file_name = NULL;
194 *type = USTAR_EOF;
195 *size = 0;
196 return NULL;
197 }
198
199 /* Validate ustar header. */
200 if (memcmp (h->magic, "ustar", 6))
201 return "not a ustar archive";
202 else if (h->version[0] != '0' || h->version[1] != '0')
203 return "invalid ustar version";
204 else if (!parse_octal_field (h->chksum, sizeof h->chksum, &chksum))
205 return "corrupt chksum field";
206 else if (chksum != calculate_chksum (h))
207 return "checksum mismatch";
208 else if (h->name[sizeof h->name - 1] != '\0' || h->prefix[0] != '\0')
209 return "file name too long";
210 else if (h->typeflag != USTAR_REGULAR && h->typeflag != USTAR_DIRECTORY)
211 return "unimplemented file type";
212 if (h->typeflag == USTAR_REGULAR)
213 {
214 if (!parse_octal_field (h->size, sizeof h->size, &size_ul))
215 return "corrupt file size field";
216 else if (size_ul > INT_MAX)
217 return "file too large";
218 }
219 else
220 size_ul = 0;
221
222 /* Success. */
223 *file_name = strip_antisocial_prefixes (h->name);
224 *type = h->typeflag;
225 *size = size_ul;
226 return NULL;
227}
228
diff --git a/pintos-progos/lib/ustar.h b/pintos-progos/lib/ustar.h
new file mode 100644
index 0000000..43a5513
--- /dev/null
+++ b/pintos-progos/lib/ustar.h
@@ -0,0 +1,29 @@
1#ifndef __LIB_USTAR_H
2#define __LIB_USTAR_H
3
4/* Support for the standard Posix "ustar" format. See the
5 documentation of the "pax" utility in [SUSv3] for the the
6 "ustar" format specification. */
7
8#include <stdbool.h>
9
10/* Type of a file entry in an archive.
11 The values here are the bytes that appear in the file format.
12 Only types of interest to Pintos are listed here. */
13enum ustar_type
14 {
15 USTAR_REGULAR = '0', /* Ordinary file. */
16 USTAR_DIRECTORY = '5', /* Directory. */
17 USTAR_EOF = -1 /* End of archive (not an official value). */
18 };
19
20/* Size of a ustar archive header, in bytes. */
21#define USTAR_HEADER_SIZE 512
22
23bool ustar_make_header (const char *file_name, enum ustar_type,
24 int size, char header[USTAR_HEADER_SIZE]);
25const char *ustar_parse_header (const char header[USTAR_HEADER_SIZE],
26 const char **file_name,
27 enum ustar_type *, int *size);
28
29#endif /* lib/ustar.h */
diff --git a/pintos-progos/misc/0001-bochs-2.3.7-jitter.patch b/pintos-progos/misc/0001-bochs-2.3.7-jitter.patch
new file mode 100644
index 0000000..44190e3
--- /dev/null
+++ b/pintos-progos/misc/0001-bochs-2.3.7-jitter.patch
@@ -0,0 +1,78 @@
1From 5e6cfa27ba6de331ecc142e7f65b4d1c2112b4e2 Mon Sep 17 00:00:00 2001
2From: Alex Busenius <s9albuse@stud.uni-saarland.de>
3Date: Mon, 27 Apr 2009 15:33:37 +0200
4Subject: bochs-2.3.7 jitter
5
6---
7 bochs.h | 2 ++
8 iodev/pit82c54.cc | 9 ++++++++-
9 main.cc | 8 ++++++++
10 3 files changed, 18 insertions(+), 1 deletions(-)
11
12diff --git a/bochs.h b/bochs.h
13index 2a643cd..75bcd96 100644
14--- a/bochs.h
15+++ b/bochs.h
16@@ -630,4 +630,6 @@ void bx_center_print(FILE *file, const char *line, unsigned maxwidth);
17
18 #endif
19
20+extern int jitter;
21+
22 #endif /* BX_BOCHS_H */
23diff --git a/iodev/pit82c54.cc b/iodev/pit82c54.cc
24index 0d65768..31ac041 100644
25--- a/iodev/pit82c54.cc
26+++ b/iodev/pit82c54.cc
27@@ -28,6 +28,7 @@
28
29 #include "iodev.h"
30 #include "pit82c54.h"
31+#include <stdlib.h>
32 #define LOG_THIS this->
33
34
35@@ -399,7 +400,13 @@ pit_82C54::clock(Bit8u cnum)
36 case 2:
37 if (thisctr.count_written) {
38 if (thisctr.triggerGATE || thisctr.first_pass) {
39- set_count(thisctr, thisctr.inlatch);
40+ unsigned n = thisctr.inlatch;
41+ if (jitter && n > 5) {
42+ n *= (double) rand() / RAND_MAX;
43+ if (n < 5)
44+ n = 5;
45+ }
46+ set_count(thisctr, n);
47 thisctr.next_change_time=(thisctr.count_binary-1) & 0xFFFF;
48 thisctr.null_count=0;
49 if (thisctr.inlatch==1) {
50diff --git a/main.cc b/main.cc
51index ebdf258..09cf661 100644
52--- a/main.cc
53+++ b/main.cc
54@@ -112,6 +112,7 @@ BOCHSAPI BX_MEM_C bx_mem;
55 #endif
56
57 char *bochsrc_filename = NULL;
58+int jitter = 0;
59
60 void bx_print_header ()
61 {
62@@ -541,6 +542,13 @@ int bx_init_main(int argc, char *argv[])
63 else if (!strcmp("-q", argv[arg])) {
64 SIM->get_param_enum(BXPN_BOCHS_START)->set(BX_QUICK_START);
65 }
66+ else if (!strcmp ("-j", argv[arg])) {
67+ if (++arg >= argc) BX_PANIC(("-j must be followed by a number"));
68+ else {
69+ jitter = 1;
70+ srand(atoi(argv[arg]));
71+ }
72+ }
73 else if (!strcmp("-f", argv[arg])) {
74 if (++arg >= argc) BX_PANIC(("-f must be followed by a filename"));
75 else bochsrc_filename = argv[arg];
76--
771.6.2.3
78
diff --git a/pintos-progos/misc/0002-bochs-2.3.7-triple-fault.patch b/pintos-progos/misc/0002-bochs-2.3.7-triple-fault.patch
new file mode 100644
index 0000000..c8698bd
--- /dev/null
+++ b/pintos-progos/misc/0002-bochs-2.3.7-triple-fault.patch
@@ -0,0 +1,87 @@
1From 356b7e781c815c70c992d58360caa42f1776d06b Mon Sep 17 00:00:00 2001
2From: Alex Busenius <s9albuse@stud.uni-saarland.de>
3Date: Mon, 27 Apr 2009 17:09:27 +0200
4Subject: bochs-2.3.7 triple fault
5
6---
7 cpu/cpu.h | 4 ++++
8 cpu/exception.cc | 7 +++++++
9 gdbstub.cc | 11 ++++++++---
10 3 files changed, 19 insertions(+), 3 deletions(-)
11
12diff --git a/cpu/cpu.h b/cpu/cpu.h
13index 7c7b11b..c47133a 100644
14--- a/cpu/cpu.h
15+++ b/cpu/cpu.h
16@@ -903,6 +903,10 @@ public: // for now...
17 #endif
18 Bit8u trace;
19
20+#if BX_GDBSTUB
21+ Bit8u ispanic;
22+#endif
23+
24 // for paging
25 struct {
26 bx_TLB_entry entry[BX_TLB_SIZE] BX_CPP_AlignN(16);
27diff --git a/cpu/exception.cc b/cpu/exception.cc
28index c3e3777..fb3abfc 100644
29--- a/cpu/exception.cc
30+++ b/cpu/exception.cc
31@@ -856,6 +856,13 @@ void BX_CPU_C::exception(unsigned vector, Bit16u error_code, bx_bool trap)
32 // trap into debugger (similar as done when PANIC occured)
33 bx_debug_break();
34 #endif
35+#if BX_GDBSTUB
36+ if (bx_dbg.gdbstub_enabled) {
37+ fprintf(stderr, "Triple fault: stopping for gdb\n");
38+ BX_CPU_THIS_PTR ispanic = 1;
39+ longjmp(BX_CPU_THIS_PTR jmp_buf_env, 1);
40+ }
41+#endif
42 if (SIM->get_param_bool(BXPN_RESET_ON_TRIPLE_FAULT)->get()) {
43 BX_ERROR(("exception(): 3rd (%d) exception with no resolution, shutdown status is %02xh, resetting", vector, DEV_cmos_get_reg(0x0f)));
44 bx_pc_system.Reset(BX_RESET_SOFTWARE);
45diff --git a/gdbstub.cc b/gdbstub.cc
46index f58f866..bc5ed61 100644
47--- a/gdbstub.cc
48+++ b/gdbstub.cc
49@@ -471,7 +471,12 @@ static void debug_loop(void)
50 }
51
52 stub_trace_flag = 0;
53+ bx_cpu.ispanic = 0;
54 bx_cpu.cpu_loop(0);
55+ if (bx_cpu.ispanic)
56+ {
57+ last_stop_reason = GDBSTUB_EXECUTION_BREAKPOINT;
58+ }
59
60 DEV_vga_refresh();
61
62@@ -502,19 +507,19 @@ static void debug_loop(void)
63
64 BX_INFO(("stepping"));
65 stub_trace_flag = 1;
66+ bx_cpu.ispanic = 0;
67 bx_cpu.cpu_loop(0);
68 DEV_vga_refresh();
69 stub_trace_flag = 0;
70 BX_INFO(("stopped with %x", last_stop_reason));
71 buf[0] = 'S';
72- if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT ||
73- last_stop_reason == GDBSTUB_TRACE)
74+ if (last_stop_reason == GDBSTUB_TRACE && !bx_cpu.ispanic)
75 {
76 write_signal(&buf[1], SIGTRAP);
77 }
78 else
79 {
80- write_signal(&buf[1], SIGTRAP);
81+ write_signal(&buf[1], SIGSEGV);
82 }
83 put_reply(buf);
84 break;
85--
861.6.2.3
87
diff --git a/pintos-progos/misc/0003-bochs-2.3.7-page-fault-segv.patch b/pintos-progos/misc/0003-bochs-2.3.7-page-fault-segv.patch
new file mode 100644
index 0000000..8b6e090
--- /dev/null
+++ b/pintos-progos/misc/0003-bochs-2.3.7-page-fault-segv.patch
@@ -0,0 +1,93 @@
1From 314833401978558db06bbb4f4f76e4dc7b603744 Mon Sep 17 00:00:00 2001
2From: Alex Busenius <s9albuse@stud.uni-saarland.de>
3Date: Mon, 27 Apr 2009 16:33:54 +0200
4Subject: bochs-2.3.7 page fault segv
5
6---
7 bochs.h | 1 +
8 cpu/exception.cc | 4 ++++
9 gdbstub.cc | 17 ++++++++++++++++-
10 3 files changed, 21 insertions(+), 1 deletions(-)
11
12diff --git a/bochs.h b/bochs.h
13index 75bcd96..657c7b8 100644
14--- a/bochs.h
15+++ b/bochs.h
16@@ -433,6 +433,7 @@ BOCHSAPI extern logfunc_t *genlog;
17 void bx_gdbstub_init(void);
18 void bx_gdbstub_break(void);
19 int bx_gdbstub_check(unsigned int eip);
20+void bx_gdbstub_exception(unsigned int nr);
21 #define GDBSTUB_STOP_NO_REASON (0xac0)
22
23 #if BX_SUPPORT_SMP
24diff --git a/cpu/exception.cc b/cpu/exception.cc
25index fb3abfc..8dac5ca 100644
26--- a/cpu/exception.cc
27+++ b/cpu/exception.cc
28@@ -1046,6 +1046,10 @@ void BX_CPU_C::exception(unsigned vector, Bit16u error_code, bx_bool trap)
29
30 BX_CPU_THIS_PTR errorno++;
31
32+#if BX_GDBSTUB
33+ bx_gdbstub_exception(vector);
34+#endif
35+
36 if (real_mode()) {
37 // not INT, no error code pushed
38 BX_CPU_THIS_PTR interrupt(vector, 0, 0, 0);
39diff --git a/gdbstub.cc b/gdbstub.cc
40index bc5ed61..ad59373 100644
41--- a/gdbstub.cc
42+++ b/gdbstub.cc
43@@ -47,6 +47,7 @@ static int last_stop_reason = GDBSTUB_STOP_NO_REASON;
44 #define GDBSTUB_EXECUTION_BREAKPOINT (0xac1)
45 #define GDBSTUB_TRACE (0xac2)
46 #define GDBSTUB_USER_BREAK (0xac3)
47+#define GDBSTUB_EXCEPTION_0E (0xac4)
48
49 static bx_list_c *gdbstub_list;
50 static int listen_socket_fd;
51@@ -323,6 +324,12 @@ int bx_gdbstub_check(unsigned int eip)
52 return GDBSTUB_STOP_NO_REASON;
53 }
54
55+void bx_gdbstub_exception(unsigned int nr)
56+{
57+ if (nr == 0x0e)
58+ last_stop_reason = GDBSTUB_EXCEPTION_0E;
59+}
60+
61 static int remove_breakpoint(unsigned int addr, int len)
62 {
63 unsigned int i;
64@@ -493,6 +500,10 @@ static void debug_loop(void)
65 {
66 write_signal(&buf[1], SIGTRAP);
67 }
68+ else if (last_stop_reason == GDBSTUB_EXCEPTION_0E)
69+ {
70+ write_signal(&buf[1], SIGSEGV);
71+ }
72 else
73 {
74 write_signal(&buf[1], 0);
75@@ -517,10 +528,14 @@ static void debug_loop(void)
76 {
77 write_signal(&buf[1], SIGTRAP);
78 }
79- else
80+ else if (last_stop_reason == GDBSTUB_EXCEPTION_0E)
81 {
82 write_signal(&buf[1], SIGSEGV);
83 }
84+ else
85+ {
86+ write_signal(&buf[1], 0);
87+ }
88 put_reply(buf);
89 break;
90 }
91--
921.6.2.3
93
diff --git a/pintos-progos/misc/bochs-2.3.7-build.sh b/pintos-progos/misc/bochs-2.3.7-build.sh
new file mode 100755
index 0000000..57e35d1
--- /dev/null
+++ b/pintos-progos/misc/bochs-2.3.7-build.sh
@@ -0,0 +1,42 @@
1#! /bin/sh -e
2
3if test -z "$SRCDIR" || test -z "$PINTOSDIR" || test -z "$DSTDIR"; then
4 echo "usage: env SRCDIR=<srcdir> PINTOSDIR=<srcdir> DSTDIR=<dstdir> sh $0"
5 echo " where <srcdir> contains bochs-2.3.7.tar.gz"
6 echo " and <pintosdir> is the root of the pintos source tree"
7 echo " and <dstdir> is the installation prefix (e.g. /usr/local)"
8 exit 1
9fi
10
11cd /tmp
12mkdir bochs-pintos-$$
13cd bochs-pintos-$$
14mkdir bochs-2.3.7
15tar xzf $SRCDIR/bochs-2.3.7.tar.gz
16cd bochs-2.3.7
17cat $PINTOSDIR/src/misc/0001-bochs-2.3.7-jitter.patch | patch -p1
18cat $PINTOSDIR/src/misc/0002-bochs-2.3.7-triple-fault.patch | patch -p1
19cat $PINTOSDIR/src/misc/0003-bochs-2.3.7-page-fault-segv.patch | patch -p1
20cat $PINTOSDIR/src/misc/bochs-2.3.7-gcc43.patch | patch -p1
21cat $PINTOSDIR/src/misc/bochs-2.3.7-typos.patch | patch -p1
22cat $PINTOSDIR/src/misc/bochs-2.3.7-linux3x.patch | patch -p1
23autoconf
24
25CFGOPTIONAL="--enable-large-pages --enable-mmx --enable-usb --enable-pci --enable-pcidev --enable-acpi --enable-global-pages --enable-show-ips"
26CFGOPTIMIZE="--enable-all-optimizations --enable-guest2host-tlb --enable-repeat-speedups --enable-trace-cache --enable-icache --enable-fast-function-calls --enable-idle-hack "
27CFGOPTS="--prefix=$DSTDIR --enable-ignore-bad-msr --enable-disasm --enable-logging --enable-fpu --enable-alignment-check --enable-plugins --enable-cpu-level=6 --enable-readline --without-sdl --without-svga --without-wx --with-x --with-x11 --with-term --with-nogui $CFGOPTIONAL"
28mkdir plain &&
29 cd plain &&
30 ../configure $CFGOPTS --enable-gdb-stub &&
31# make -j3 &&
32 make && echo "done building plain" &&
33 sudo make install &&
34 cd .. &&
35mkdir with-dbg &&
36 cd with-dbg &&
37 ../configure --enable-debugger $CFGOPTS &&
38 # make -j3 &&
39 make && echo "done building with-dbg" &&
40 sudo cp -v bochs $DSTDIR/bin/bochs-dbg &&
41 cd .. &&
42 echo "SUCCESS"
diff --git a/pintos-progos/misc/bochs-2.3.7-gcc43.patch b/pintos-progos/misc/bochs-2.3.7-gcc43.patch
new file mode 100644
index 0000000..4646edf
--- /dev/null
+++ b/pintos-progos/misc/bochs-2.3.7-gcc43.patch
@@ -0,0 +1,12 @@
1--- bochs-2.3.7.orig/bx_debug/symbols.cc 2008/03/30 14:32:14 1.11
2+++ bochs-2.3.7/bx_debug/symbols.cc 2008/06/16 17:09:52 1.12
3@@ -95,6 +95,9 @@
4 #endif
5
6 using namespace std;
7+#ifdef __GNUC__
8+using namespace __gnu_cxx;
9+#endif
10
11 struct symbol_entry_t
12 {
diff --git a/pintos-progos/misc/bochs-2.3.7-linux3x.patch b/pintos-progos/misc/bochs-2.3.7-linux3x.patch
new file mode 100644
index 0000000..1c84060
--- /dev/null
+++ b/pintos-progos/misc/bochs-2.3.7-linux3x.patch
@@ -0,0 +1,11 @@
1--- a/configure.in 2012-01-03 11:12:22.104612131 +0100
2+++ b/configure.in 2012-01-03 11:13:05.507941106 +0100
3@@ -715,7 +715,7 @@ AC_ARG_ENABLE(pcidev,
4 PCIDEV_MODULE_MAKE_ALL="all-kernel24"
5 KERNEL_MODULE_SUFFIX="o"
6 ;;
7- 2.6*)
8+ 2.6*|3*)
9 PCIDEV_MODULE_MAKE_ALL="all-kernel26"
10 KERNEL_MODULE_SUFFIX="ko"
11 ;;
diff --git a/pintos-progos/misc/bochs-2.3.7-typos.patch b/pintos-progos/misc/bochs-2.3.7-typos.patch
new file mode 100644
index 0000000..c9fb168
--- /dev/null
+++ b/pintos-progos/misc/bochs-2.3.7-typos.patch
@@ -0,0 +1,24 @@
1diff -NaurwB bochs-2.3.7.orig/cpu/ia_opcodes.h bochs-2.3.7/cpu/ia_opcodes.h
2--- bochs-2.3.7.orig/cpu/ia_opcodes.h 2008-05-30 22:35:08.000000000 +0200
3+++ bochs-2.3.7/cpu/ia_opcodes.h 2008-06-04 14:56:46.000000000 +0200
4@@ -891,7 +891,7 @@
5 bx_define_opcode(BX_IA_PF2ID_PqQq, BX_CPU_C::PF2ID_PqQq)
6 bx_define_opcode(BX_IA_PF2IW_PqQq, BX_CPU_C::PF2IW_PqQq)
7 bx_define_opcode(BX_IA_PFACC_PqQq, BX_CPU_C::PFACC_PqQq)
8-bx_define_opcode(BX_IA_PFADD_PqQq, BX_CPU_C::BX_PFADD_PqQq)
9+bx_define_opcode(BX_IA_PFADD_PqQq, BX_CPU_C::PFADD_PqQq)
10 bx_define_opcode(BX_IA_PFCMPEQ_PqQq, BX_CPU_C::PFCMPEQ_PqQq)
11 bx_define_opcode(BX_IA_PFCMPGE_PqQq, BX_CPU_C::PFCMPGE_PqQq)
12 bx_define_opcode(BX_IA_PFCMPGT_PqQq, BX_CPU_C::PFCMPGT_PqQq)
13diff -NaurwB bochs-2.3.7.orig/iodev/iodebug.h bochs-2.3.7/iodev/iodebug.h
14--- bochs-2.3.7.orig/iodev/iodebug.h 2008-05-01 22:46:58.000000000 +0200
15+++ bochs-2.3.7/iodev/iodebug.h 2008-06-04 14:45:50.000000000 +0200
16@@ -18,7 +18,7 @@
17 virtual void init(void);
18 virtual void reset (unsigned type) {}
19 static void mem_write(BX_CPU_C *cpu, bx_phy_address addr, unsigned len, void *data);
20- static void mem_read(BX_CPU_C *cpu, bx_phy_addressu addr, unsigned len, void *data);
21+ static void mem_read(BX_CPU_C *cpu, bx_phy_address addr, unsigned len, void *data);
22
23 private:
24 static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
diff --git a/pintos-progos/misc/gcc-3.3.6-cross-howto b/pintos-progos/misc/gcc-3.3.6-cross-howto
new file mode 100644
index 0000000..ad25173
--- /dev/null
+++ b/pintos-progos/misc/gcc-3.3.6-cross-howto
@@ -0,0 +1,39 @@
1Here are the commands we used to build and install the SPARC
2cross-compiler:
3
4PINTOSROOT=$HOME/private/pintos
5
6PREFIX=/usr/class/cs140/`uname -m`
7PATH=$PATH:$PREFIX/bin
8TMP=`pwd`
9
10wget ftp://ftp.gnu.org/pub/gnu/binutils/binutils-2.15.tar.bz2
11wget ftp://sources.redhat.com/pub/newlib/newlib-1.13.0.tar.gz
12wget ftp://ftp.gnu.org/pub/gnu/gcc/gcc-3.3.6/gcc-core-3.3.6.tar.bz2
13wget ftp://ftp.gnu.org/pub/gnu/gdb/gdb-6.3.tar.bz2
14
15bzcat binutils-2.15.tar.bz2 | tar x
16tar xzf newlib-1.13.0.tar.gz
17bzcat gcc-core-3.3.6.tar.bz2 | tar x
18bzcat gdb-6.3.tar.bz2 | tar x
19
20cd $TMP/binutils-2.15
21mkdir i386
22cd i386
23../configure --target=i386-elf --prefix=$PREFIX
24make LDFLAGS=-lintl
25make install
26
27cd $TMP/gcc-3.3.6
28mkdir i386
29cd i386
30../configure --target=i386-elf --prefix=$PREFIX --with-gnu-as --with-as=$PREFIX/bin/i386-elf-as --with-gnu-ld --with-ld=$PREFIX/bin/i386-elf-ld --with-headers=$TMP/newlib-1.13.0/newlib/libc/include --with-newlib
31make
32make install
33
34cd $TMP/gdb-6.3
35mkdir i386
36cd i386
37../configure --target=i386-elf --prefix=$PREFIX --disable-tui
38make LDFLAGS=-lintl
39make install
diff --git a/pintos-progos/misc/gdb-macros b/pintos-progos/misc/gdb-macros
new file mode 100644
index 0000000..a0b68c3
--- /dev/null
+++ b/pintos-progos/misc/gdb-macros
@@ -0,0 +1,140 @@
1#
2# A set of useful macros that can help debug Pintos.
3#
4# Include with "source" cmd in gdb.
5# Use "help user-defined" for help.
6#
7# Author: Godmar Back <gback@cs.vt.edu>, Feb 2006
8#
9# $Id: gdb-macros,v 1.1 2006-04-07 18:29:34 blp Exp $
10#
11
12# for internal use
13define offsetof
14 set $rc = (char*)&((struct $arg0 *)0)->$arg1 - (char*)0
15end
16
17define list_entry
18 offsetof $arg1 $arg2
19 set $rc = ((struct $arg1 *) ((uint8_t *) ($arg0) - $rc))
20end
21
22# dump a Pintos list
23define dumplist
24 set $list = $arg0
25 set $e = $list->head.next
26 set $i = 0
27 while $e != &(($arg0).tail)
28 list_entry $e $arg1 $arg2
29 set $l = $rc
30 printf "pintos-debug: dumplist #%d: %p ", $i++, $l
31 output *$l
32 set $e = $e->next
33 printf "\n"
34 end
35end
36
37document dumplist
38 Dump the content of a Pintos list,
39 invoke as dumplist name_of_list name_of_struct name_of_elem_in_list_struct
40end
41
42# print a thread's backtrace, given a pointer to the struct thread *
43define btthread
44 if $arg0 == ($esp - ((unsigned)$esp % 4096))
45 bt
46 else
47 set $saveEIP = $eip
48 set $saveESP = $esp
49 set $saveEBP = $ebp
50
51 set $esp = ((struct thread *)$arg0)->stack
52 set $ebp = ((void**)$esp)[2]
53 set $eip = ((void**)$esp)[4]
54
55 bt
56
57 set $eip = $saveEIP
58 set $esp = $saveESP
59 set $ebp = $saveEBP
60 end
61end
62document btthread
63 Show the backtrace of a thread,
64 invoke as btthread pointer_to_struct_thread
65end
66
67# print backtraces associated with all threads in a list
68define btthreadlist
69 set $list = $arg0
70 set $e = $list->head.next
71 while $e != &(($arg0).tail)
72 list_entry $e thread $arg1
73 printf "pintos-debug: dumping backtrace of thread '%s' @%p\n", \
74 ((struct thread*)$rc)->name, $rc
75 btthread $rc
76 set $e = $e->next
77 printf "\n"
78 end
79end
80document btthreadlist
81 Given a list of threads, print each thread's backtrace
82 invoke as btthreadlist name_of_list name_of_elem_in_list_struct
83end
84
85# print backtraces of all threads (based on 'all_list' all threads list)
86define btthreadall
87 btthreadlist all_list allelem
88end
89document btthreadall
90 Print backtraces of all threads
91end
92
93# print a correct backtrace by adjusting $eip
94# this works best right at intr0e_stub
95define btpagefault
96 set $saveeip = $eip
97 set $eip = ((void**)$esp)[1]
98 backtrace
99 set $eip = $saveeip
100end
101document btpagefault
102 Print a backtrace of the current thread after a pagefault
103end
104
105# invoked whenever the program stops
106define hook-stop
107 # stopped at stub #0E = #14 (page fault exception handler stub)
108 if ($eip == intr0e_stub)
109 set $savedeip = ((void**)$esp)[1]
110 # if this was in user mode, the OS should handle it
111 # either handle the page fault or terminate the process
112 if ($savedeip < 0xC0000000)
113 printf "pintos-debug: a page fault exception occurred in user mode\n"
114 printf "pintos-debug: hit 'c' to continue, or 's' to step to intr_handler\n"
115 else
116 # if this was in kernel mode, a stack trace might be useful
117 printf "pintos-debug: a page fault occurred in kernel mode\n"
118 btpagefault
119 end
120 end
121end
122
123# load symbols for a Pintos user program
124define loadusersymbols
125 shell objdump -h $arg0 | awk '/.text/ { print "add-symbol-file $arg0 0x"$4 }' > .loadsymbols
126 source .loadsymbols
127 shell rm -f .loadsymbols
128end
129document loadusersymbols
130 Load the symbols contained in a user program's executable.
131 Example:
132 loadusersymbols tests/userprog/exec-multiple
133end
134
135define debugpintos
136 target remote localhost:1234
137end
138document debugpintos
139 Attach debugger to pintos process
140end
diff --git a/pintos-progos/notes/1.txt b/pintos-progos/notes/1.txt
new file mode 100644
index 0000000..9d478f7
--- /dev/null
+++ b/pintos-progos/notes/1.txt
@@ -0,0 +1,81 @@
1Getting Started with PINTOS
2===========================
3
4Building Project 1
5------------------
6
7pintos $ cd src/threads
8threads $ make
9
10
11Building Bochs
12--------------
13You should have a patched bochs install available.
14
15See
16
17 http://courses.mpi-sws.org/os-ss11/assignments/pintos/pintos_12.html#SEC160
18
19There is a build script src/misc/bochs-2.3.7-build.sh in the pintos fork from Saarland,
20which (after small modifications) works on a modern Ubuntu x86.
21
22For Ubuntu 11 with a Linux 3.0 kernel, you need to:
23
24 * Regenerate the configure script (autoconf configure.in)
25 * Patch the test for Linux 2.4 or 2.6
26
27After building, copy bochs and bochs-gdb to the pintos/src/utils directory
28
29Running
30-------
31
32 # [pintos/src]
33 PATH=`pwd`/utils:$PATH
34
35 cd threads/build
36 # [pintos/src/threads/build]
37 pintos run alarm-multiple > logfile
38
39
40### Reproducability
41
42This command line flags to pintos influence reproducability.
43Remember: you need the patched bochs build.
44
45 -j seed ... Reproducible behavior
46 -r ... Real-Time behavior
47
48Running with qemu
49-----------------
50
51 # [pintos/src]
52 vim utils/pintos # comment line with -no-kqemu flag
53
54 cd threads/build
55 # [pintos/src/threads/build]
56 pintos --qemu -- run alarm-multiple
57
58Debugging
59---------
60
61pintos $ vim utils/pintos-gdb
62
63 GDBMACROS=${PINTOS_SRC}/misc/gdb-macros
64
65[pts/0 build] $ pintos --gdb -- run alarm-multiple
66[pts/1 build] $ pintos-gdb kernel.o
67(gdb) debugpintos
68
69Testing
70-------
71
72* Running all tests
73
74 build $ make check
75
76* Running a single test
77
78 build $ rm tests/threads/alarm-multiple.result
79 build $ make tests/threads/alarm-multiple.result
80
81
diff --git a/pintos-progos/notes/2.txt b/pintos-progos/notes/2.txt
new file mode 100644
index 0000000..c81b980
--- /dev/null
+++ b/pintos-progos/notes/2.txt
@@ -0,0 +1,164 @@
1Projekt 1 - Threads
2===================
3
4alarm clock
5-----------
6The simplest strategy is to maintain a wait list for
7all threads blocked for sleep.
8
9 * In 'timer_interrupt', check for threads which can be
10 unblocked from sleeping
11 * In 'sleep', set sleep timeout in thread, block the
12 thread and put it on the sleep list
13
14Notes:
15
16 * There are three places where a thread is added to the
17 ready list:
18 - thread_init
19 - thread_yield
20 - thread_unblock
21 * Iterate list with removal:
22 for (e = list_begin (&list); e != list_end (&list); )
23 if(...)
24 e = list_remove(e)->prev;
25 /* Unblock must be called AFTER removing, as thread.elem is reused */
26 else
27 e = list_next(e);
28
29Stats:
30
31 pintos/src/devices/timer.c | 40 ++++++++++++++++++++++++++++++++++++++--
32 pintos/src/threads/thread.h | 3 +++
33 2 files changed, 41 insertions(+), 2 deletions(-)
34
35 Design & Implementation time: 4 hours
36
37Priority Scheduler
38------------------
39
40A simple implementation of the priority scheduler (64 priority levels, round robin within
41one priority group).
42
43 * If a new task arrives with a higher priority, switch to this group
44 * If the currently active group is empty, search for the group with the next highest priority
45
46Notes:
47
48 * thread_{init,unblock,yield} now call thread_ready, which updates the lowest ready priority
49 * The thread_unblock operation does not yield a new thread immediately. Therefore, we need to check
50 later whether we need to switch to a higher priority thread (via thread_yield).
51 As thread_unblock is called with interrupts off, it seemed best to perform
52 this check when interrupts are enabled. This is only necessary if a higher priority task
53 is ready.
54 * First attempt passed alarm-priority, but failed to pass the priority-preempt test.
55 But the debugging facilities are fantastic, so it was easy to spot the problem
56 * Wolfgang suggested to yield a software interrupt when unblocking instead of modifying
57 interrupt_enable.
58
59Stats:
60
61 pintos/src/threads/interrupt.c | 3 +-
62 pintos/src/threads/thread.c | 60 ++++++++++++++++++++++++++++++++--------
63 pintos/src/threads/thread.h | 1 +
64 3 files changed, 51 insertions(+), 13 deletions(-)
65
66 Design and implementation time: 3 hours
67
68Priority Locks
69--------------
70
71We also need to select higher priority task first from locks, semaphores and condition variables.
72This easiest implementation searches for the thread with the highest priority in the wait queue.
73
74Notes:
75
76 * It is sufficient to implement the priority based selection twice, for sema_up and
77 cond_signal. cond_signal is a little bit harder, as we need to store the priority
78 (or the waiting thread) in the semaphore_elem type
79 * There are some handy list utility functions; in this case, list_max does a fine job
80 for both sema_up and cond_signal
81 * It is difficult to implement this in an efficient (sublinear) way, because priority donation
82 may boost a thread at any time!
83
84Stats:
85
86 pintos/src/threads/synch.c | 40 ++++++++++++++++++++++++++++++++++------
87 1 files changed, 34 insertions(+), 6 deletions(-)
88
89 Design and Implementation time: 1 hour
90
91Priority Donation
92-----------------
93If a thread aquires a lock, the lock holder needs to be boosted to the donated priority.
94We need to deal with nesting and chaining:
95
96 * Lock/Thread correspondence: Each lock is associated with at most one thread that holds it.
97 Therefore, donated priority can be associated with a lock.
98 * If a thread t wants to obtain a lock L, and a thread with a lower priority holds it,
99 the thread holding the lock is boosted to the priority of the requesting thread
100 * Chaining: If the boosted thread is also blocked on a lock, than we also need to donate
101 the priority to that lock, in a transitive way.
102 * Nesting: If a thread may hold more than one lock, we need to keep track of the donation
103 to each lock. When a lock is released or the static priority changes, the highest priority
104 donated to other locks is assigned to the thread.
105
106With this information, the following rules seem suitable (without proof of correctness):
107
108 * If thread T tries to aquire lock L
109
110 ==> if(L.owner)
111 T.locked_on = L
112 donate_priority (L, T.priority)
113 end
114 donate_priority (L ,p) :=
115 L.priority v= p
116 L.holder.priority v= p
117 donate_priority( L.holder.locked_on, p)
118 end
119
120 * If a thread T aquires a lock L
121
122 ==> L.holder = T
123 T.locks += L
124 T.lock_on = none
125
126
127 * If a thread T releases a lock L
128
129 ==> L.holder = none
130 T.locks -= L
131 T.priority = max (T.locks.priority, static_priority)
132
133To implement this, each thread needs to maintain a list of locks, a static priority,
134and a reference to the lock blocking it.
135
136Notes:
137
138 * Difficult to design, really a challenge.
139 Literature (short paper) could be helpful.
140 Maybe it would be interesting to ask for correctness proof sketches?
141
142 * First design was wrong, because I assumed locks are aquired in FIFO fashion
143 * First implementation failed to pass donate tests, due to a typo. Debugging
144 facilities are fantastic, no problem to spot this one.
145 * Next try, priority-donate-lower failed. Looking at the source code revealed that
146 we need to recompute the priority at the end of thread_set_priority.
147 * Next try, priority-donate-chain failed. Chaining is tricky to get right;
148 in my implementation, chained donations were lost after one lock was released.
149
150 * It would be an interesting option to ask for new test cases from the students
151 * I think it would also be a cool task to write a test for a RMS scheduling
152 scenario with blocking.
153
154Stats:
155
156 pintos/src/threads/synch.c | 15 ++++++++++++
157 pintos/src/threads/synch.h | 6 +++-
158 pintos/src/threads/thread.c | 53 +++++++++++++++++++++++++++++++++++++++++-
159 pintos/src/threads/thread.h | 9 ++++++-
160 pintos/src/utils/pintos | 2 +-
161
162 Design: 5h
163 Implementation: 3h
164
diff --git a/pintos-progos/notes/3.txt b/pintos-progos/notes/3.txt
new file mode 100644
index 0000000..bc64f88
--- /dev/null
+++ b/pintos-progos/notes/3.txt
@@ -0,0 +1,241 @@
1Project 2
2=========
3
4Working with Disks
5------------------
6
7Assumes you ran make in src/userprog and src/examples.
8
9 * Create a 2 MB hard disk for pintos
10
11 # [src/userprog/build]
12 pintos-mkdisk filesys.dsk --filesys-size=2
13
14 * Format Disk
15
16 # -f ... format virtual disk
17 pintos -f -q
18
19 * Copy file to filesystem
20
21 # -p FILE ... file to put on virtual disk
22 # -a FILE ... newname on virtual disk
23 pintos -p ../../examples/echo -a echo -- -q
24
25 * Execute echo, and get file 'echo' from the virtual disk
26
27 pintos -g echo -- -q run 'echo x'
28
29Putting all together, we can run an minimal example like that:
30
31 # [src/userprog/build]
32 pintos --filesys-size=2 -p ../../examples/halt -a halt -- -f -q run 'halt'
33
34Getting Started
35---------------
36
37 * Fix the problem with the .note.gnu.build-id segment
38
39 * Change the stack setup in process.c#setup_stack() to
40
41 *esp = PHYS_BASE - 12;
42
43 * Change process_wait() to an infinite loop
44
45This should be enough to see 'system call!' when executing
46the 'halt' example.
47
48Next, we need to implement user memory access and the
49the system call dispatcher, as well as the basic
50system calls halt, exit and write.
51
52A simple implementation of user memory access first checks
53whether the address is in user space, and the calls load_page.
54
55For an initial system call dispatcher, we convert the stack pointer
56saved by the processor during the interrupt to kernel space, and
57then dispatch to halt, exit and write. For now, exit just terminates
58the process, and write uses printf, ignoring the fd argument.
59The return value is stored into %eax.
60
61Notes:
62 * halt(): There is no function shutdown() in init.h, only
63 shutdown_poweroff in shutdown.h
64
65 * When accessing data from user space in kernel space, we need to be
66 sure that the entire address ranged accessed is in user space.
67 Note that pointers are not necessarily aligned, and thus might
68 involve two user pages.
69 Furthermore, buffers need to be copied to kernel space;
70 otherwise, concurrent user space operations could corrupt the kernel.
71 Linux allows at most one kernel page for such buffers; we follow
72 the same route.
73
74 * Debugging: the function hex_dump() is useful; no need to
75 reimplement it.
76
77 * Something went wrong with the write system call, and this
78 is rather tricky to debug.
79 I invoked the system call directly, using inline
80 assembly; this worked fine?
81 Then I tried to debug the user space program; to this
82 end, lookup the code address you are interested in,
83 and use gdb together with objdump for debugging:
84
85 Debugging 'write(1,"USA\n",4)'
86
87 break *0x0804820e # break at <write>
88 cont # pushl 0xc(%esp)
89 info registers # esp = 0xbfffffbc
90 x/1w (0xbfffffbc+0xc) # ==> 4 (length)
91 stepi # pushl 0x8(%esp)
92 info registers # esp = 0x......b8
93 x/1w 0xbfffffb8 # ==> 4 (TOS)
94 x/1w (0xbfffffb8+8) # ==> 1 (wrong) !!!
95
96 Apparently, the inline assembler in pintos does not use
97 the right constraints.
98
99Stat:
100 pintos/src/lib/user/syscall.c | 6 +-
101 pintos/src/userprog/process.c | 5 ++-
102 pintos/src/userprog/syscall.c | 92 ++++++++++++++++++++++++++++++++++++++--
103
104 Reading and Implementation Time: 6 hours
105 Debugging Syscalls: 5 hours
106
107
108Argument Passing
109----------------
110First, we tokenize the command using strtok_r, and then setup
111the stack.
112
113Notes:
114 * As noted in the doc, just using strtok_r seems fine.
115 However, as strtok_r modifies the string even if only
116 the first token is needed, some copying is involved
117 if it is used to obtain the filename.
118 * Due to the detailed description in the documentation,
119 setting up the stack is mostly implementation work.
120 * One of the trickier implementation aspects is that we
121 modify the stack in kernel space, but need to convert
122 pointers to user space before pushing them on the stack.
123 * Debugging: Optimizations were really troublesome debugging
124 this task; making setup_stack non-static at least helped
125 to set a suitable breakpoint. In the end, printf was the
126 better debugging aid for this task.
127
128Stat:
129 pintos/src/userprog/process.c | 116 +++++++++++++++++++++++++++++++++--------
130
131 Design and Implementation Time: 4 hours
132
133
134Process Management: exec, wait and exit
135---------------------------------------
136The wait system call requires that all children
137of a process are known, that the exit code of
138a process is stored until collected by the parent,
139and that the parent can block until the child
140process terminates.
141
142One difficult aspect in the design is that kernel
143threads are not processes, and that child threads
144may exit after their parent. It is important to
145note that threads do not need to wait for their
146children, but that we need to keep the exit status
147until the parent exits.
148
149In the original design, a thread is cleaned up when
150in the scheduler right after it died. In our design
151we delay the cleanup if the parent thread is still alive.
152
153Another issue is that thread_create needs to block
154until the load process of the child thread has finished.
155
156Notes:
157 * I wanted to use the same semaphore for startup and wait.
158 This works, but we need one additional variable (or bit)
159 to distinguish failure at load time from failure at
160 runtime.
161 * Ugly 1: thread_create only gives back a tid,
162 so it is not possible to directly access the semaphore
163 in process_execute. Therefore we need to iterate over the
164 child list (which is not that bad, because if loading failed,
165 the child needs to be removed from the list anyway).
166 * Ugly 2: We up the semaphore used to synchronize
167 with process_execute and process_wait in thread.c, for
168 all threads.
169 * As also noted by rene, it is important to identifiy memory leaks,
170 as early as possible. To this end, first add debug messages to
171 page_alloc/page_free, and then run test programs to identify leaking
172 pages. Then debug, add conditional breakpoints to stop when a leaking
173 page is allocated, and inspect the stacktrace to find the culprit.
174
175Stats:
176 pintos/src/threads/thread.c | 31 +++++++++++++++++---
177 pintos/src/threads/thread.h | 8 +++++
178 pintos/src/userprog/process.c | 60 ++++++++++++++++++++++++++++++++--------
179 pintos/src/userprog/syscall.c | 19 +++++++++---
180
181 Design and Implementation Time: 7 hours
182
183File I/O System Calls
184---------------------
185For file I/O we need to implement synchronization (filesys is not thread safe).
186The documentation states that it is not recommended to modify the code in
187the filesys directory for now. A very simple solution is to use one lock for all filesystem operations, including process.c#load.
188Furthermore, we need to deny writes to a a file currently running as a user
189space process.
190
191Notes:
192 * init_thread() must not aquire locks, and thus not allocate pages.
193 Otherwise, the initialization of the init thread fails.
194 * The {lg,sm}-full tests failed in the initial implementation;
195 apparently, the read/write system calls should always read/write the
196 full amount of bytes specified to pass this tests. This was not
197 clear from the assignment.
198 * It is not obvious that file_close calls file_allow_write. But an
199 executable should not be writeable during its execution. Therefore,
200 one needs to make sure that it stays write protected after loading
201 has finished. I solve this by keeping the executable open during
202 execution.
203 * The multi-oom test failed again; debugging revealed that I forgot
204 to close all files at process_exit.
205
206Stats:
207
208 pintos/src/threads/thread.c | 1 +
209 pintos/src/threads/thread.h | 6 +-
210 pintos/src/userprog/process.c | 53 ++++-
211 pintos/src/userprog/process.h | 2 +
212 pintos/src/userprog/syscall.c | 435 +++++++++++++++++++++++++++++++-----
213 pintos/src/userprog/syscall.h | 1 +
214 6 files changed, 381 insertions(+), 117 deletions(-)
215 Design and Implementation Time: 8 hours
216
217Improved User Memory Access
218---------------------------
219Looking at Project 3, it is a much better idea to not check whether a user
220space page is valid, but just let the page fault handler do the job.
221I decided to exit the process in the page fault handler if the address
222is in user space. One needs to take care of temporary memory allocated
223by the syscall handler, to avoid memory leaks. To this end, temporary kernel
224pages allocated in the handler are recorded and either freed at the end
225of the syscall or the end of the process.
226
227Notes:
228 * When using this approach, it is vital to copy user buffers
229 before reading or writing. With virtual memory, a page fault may
230 require to access the file system, and thus may cause race
231 conditions during access to the file system
232
233Stats:
234 pintos/src/threads/thread.h | 5 +-
235 pintos/src/userprog/exception.c | 17 ++-
236 pintos/src/userprog/process.c | 2 +-
237 pintos/src/userprog/syscall.c | 314 +++++++++++++++++++--------------------
238 pintos/src/userprog/syscall.h | 2 +-
239 5 files changed, 173 insertions(+), 167 deletions(-)
240
241 Implementation Time: 3 hours
diff --git a/pintos-progos/tests/Algorithm/Diff.pm b/pintos-progos/tests/Algorithm/Diff.pm
new file mode 100644
index 0000000..904c530
--- /dev/null
+++ b/pintos-progos/tests/Algorithm/Diff.pm
@@ -0,0 +1,1713 @@
1package Algorithm::Diff;
2# Skip to first "=head" line for documentation.
3use strict;
4
5use integer; # see below in _replaceNextLargerWith() for mod to make
6 # if you don't use this
7use vars qw( $VERSION @EXPORT_OK );
8$VERSION = 1.19_01;
9# ^ ^^ ^^-- Incremented at will
10# | \+----- Incremented for non-trivial changes to features
11# \-------- Incremented for fundamental changes
12require Exporter;
13*import = \&Exporter::import;
14@EXPORT_OK = qw(
15 prepare LCS LCDidx LCS_length
16 diff sdiff compact_diff
17 traverse_sequences traverse_balanced
18);
19
20# McIlroy-Hunt diff algorithm
21# Adapted from the Smalltalk code of Mario I. Wolczko, <mario@wolczko.com>
22# by Ned Konz, perl@bike-nomad.com
23# Updates by Tye McQueen, http://perlmonks.org/?node=tye
24
25# Create a hash that maps each element of $aCollection to the set of
26# positions it occupies in $aCollection, restricted to the elements
27# within the range of indexes specified by $start and $end.
28# The fourth parameter is a subroutine reference that will be called to
29# generate a string to use as a key.
30# Additional parameters, if any, will be passed to this subroutine.
31#
32# my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen );
33
34sub _withPositionsOfInInterval
35{
36 my $aCollection = shift; # array ref
37 my $start = shift;
38 my $end = shift;
39 my $keyGen = shift;
40 my %d;
41 my $index;
42 for ( $index = $start ; $index <= $end ; $index++ )
43 {
44 my $element = $aCollection->[$index];
45 my $key = &$keyGen( $element, @_ );
46 if ( exists( $d{$key} ) )
47 {
48 unshift ( @{ $d{$key} }, $index );
49 }
50 else
51 {
52 $d{$key} = [$index];
53 }
54 }
55 return wantarray ? %d : \%d;
56}
57
58# Find the place at which aValue would normally be inserted into the
59# array. If that place is already occupied by aValue, do nothing, and
60# return undef. If the place does not exist (i.e., it is off the end of
61# the array), add it to the end, otherwise replace the element at that
62# point with aValue. It is assumed that the array's values are numeric.
63# This is where the bulk (75%) of the time is spent in this module, so
64# try to make it fast!
65
66sub _replaceNextLargerWith
67{
68 my ( $array, $aValue, $high ) = @_;
69 $high ||= $#$array;
70
71 # off the end?
72 if ( $high == -1 || $aValue > $array->[-1] )
73 {
74 push ( @$array, $aValue );
75 return $high + 1;
76 }
77
78 # binary search for insertion point...
79 my $low = 0;
80 my $index;
81 my $found;
82 while ( $low <= $high )
83 {
84 $index = ( $high + $low ) / 2;
85
86 # $index = int(( $high + $low ) / 2); # without 'use integer'
87 $found = $array->[$index];
88
89 if ( $aValue == $found )
90 {
91 return undef;
92 }
93 elsif ( $aValue > $found )
94 {
95 $low = $index + 1;
96 }
97 else
98 {
99 $high = $index - 1;
100 }
101 }
102
103 # now insertion point is in $low.
104 $array->[$low] = $aValue; # overwrite next larger
105 return $low;
106}
107
108# This method computes the longest common subsequence in $a and $b.
109
110# Result is array or ref, whose contents is such that
111# $a->[ $i ] == $b->[ $result[ $i ] ]
112# foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined.
113
114# An additional argument may be passed; this is a hash or key generating
115# function that should return a string that uniquely identifies the given
116# element. It should be the case that if the key is the same, the elements
117# will compare the same. If this parameter is undef or missing, the key
118# will be the element as a string.
119
120# By default, comparisons will use "eq" and elements will be turned into keys
121# using the default stringizing operator '""'.
122
123# Additional parameters, if any, will be passed to the key generation
124# routine.
125
126sub _longestCommonSubsequence
127{
128 my $a = shift; # array ref or hash ref
129 my $b = shift; # array ref or hash ref
130 my $counting = shift; # scalar
131 my $keyGen = shift; # code ref
132 my $compare; # code ref
133
134 if ( ref($a) eq 'HASH' )
135 { # prepared hash must be in $b
136 my $tmp = $b;
137 $b = $a;
138 $a = $tmp;
139 }
140
141 # Check for bogus (non-ref) argument values
142 if ( !ref($a) || !ref($b) )
143 {
144 my @callerInfo = caller(1);
145 die 'error: must pass array or hash references to ' . $callerInfo[3];
146 }
147
148 # set up code refs
149 # Note that these are optimized.
150 if ( !defined($keyGen) ) # optimize for strings
151 {
152 $keyGen = sub { $_[0] };
153 $compare = sub { my ( $a, $b ) = @_; $a eq $b };
154 }
155 else
156 {
157 $compare = sub {
158 my $a = shift;
159 my $b = shift;
160 &$keyGen( $a, @_ ) eq &$keyGen( $b, @_ );
161 };
162 }
163
164 my ( $aStart, $aFinish, $matchVector ) = ( 0, $#$a, [] );
165 my ( $prunedCount, $bMatches ) = ( 0, {} );
166
167 if ( ref($b) eq 'HASH' ) # was $bMatches prepared for us?
168 {
169 $bMatches = $b;
170 }
171 else
172 {
173 my ( $bStart, $bFinish ) = ( 0, $#$b );
174
175 # First we prune off any common elements at the beginning
176 while ( $aStart <= $aFinish
177 and $bStart <= $bFinish
178 and &$compare( $a->[$aStart], $b->[$bStart], @_ ) )
179 {
180 $matchVector->[ $aStart++ ] = $bStart++;
181 $prunedCount++;
182 }
183
184 # now the end
185 while ( $aStart <= $aFinish
186 and $bStart <= $bFinish
187 and &$compare( $a->[$aFinish], $b->[$bFinish], @_ ) )
188 {
189 $matchVector->[ $aFinish-- ] = $bFinish--;
190 $prunedCount++;
191 }
192
193 # Now compute the equivalence classes of positions of elements
194 $bMatches =
195 _withPositionsOfInInterval( $b, $bStart, $bFinish, $keyGen, @_ );
196 }
197 my $thresh = [];
198 my $links = [];
199
200 my ( $i, $ai, $j, $k );
201 for ( $i = $aStart ; $i <= $aFinish ; $i++ )
202 {
203 $ai = &$keyGen( $a->[$i], @_ );
204 if ( exists( $bMatches->{$ai} ) )
205 {
206 $k = 0;
207 for $j ( @{ $bMatches->{$ai} } )
208 {
209
210 # optimization: most of the time this will be true
211 if ( $k and $thresh->[$k] > $j and $thresh->[ $k - 1 ] < $j )
212 {
213 $thresh->[$k] = $j;
214 }
215 else
216 {
217 $k = _replaceNextLargerWith( $thresh, $j, $k );
218 }
219
220 # oddly, it's faster to always test this (CPU cache?).
221 if ( defined($k) )
222 {
223 $links->[$k] =
224 [ ( $k ? $links->[ $k - 1 ] : undef ), $i, $j ];
225 }
226 }
227 }
228 }
229
230 if (@$thresh)
231 {
232 return $prunedCount + @$thresh if $counting;
233 for ( my $link = $links->[$#$thresh] ; $link ; $link = $link->[0] )
234 {
235 $matchVector->[ $link->[1] ] = $link->[2];
236 }
237 }
238 elsif ($counting)
239 {
240 return $prunedCount;
241 }
242
243 return wantarray ? @$matchVector : $matchVector;
244}
245
246sub traverse_sequences
247{
248 my $a = shift; # array ref
249 my $b = shift; # array ref
250 my $callbacks = shift || {};
251 my $keyGen = shift;
252 my $matchCallback = $callbacks->{'MATCH'} || sub { };
253 my $discardACallback = $callbacks->{'DISCARD_A'} || sub { };
254 my $finishedACallback = $callbacks->{'A_FINISHED'};
255 my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { };
256 my $finishedBCallback = $callbacks->{'B_FINISHED'};
257 my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ );
258
259 # Process all the lines in @$matchVector
260 my $lastA = $#$a;
261 my $lastB = $#$b;
262 my $bi = 0;
263 my $ai;
264
265 for ( $ai = 0 ; $ai <= $#$matchVector ; $ai++ )
266 {
267 my $bLine = $matchVector->[$ai];
268 if ( defined($bLine) ) # matched
269 {
270 &$discardBCallback( $ai, $bi++, @_ ) while $bi < $bLine;
271 &$matchCallback( $ai, $bi++, @_ );
272 }
273 else
274 {
275 &$discardACallback( $ai, $bi, @_ );
276 }
277 }
278
279 # The last entry (if any) processed was a match.
280 # $ai and $bi point just past the last matching lines in their sequences.
281
282 while ( $ai <= $lastA or $bi <= $lastB )
283 {
284
285 # last A?
286 if ( $ai == $lastA + 1 and $bi <= $lastB )
287 {
288 if ( defined($finishedACallback) )
289 {
290 &$finishedACallback( $lastA, @_ );
291 $finishedACallback = undef;
292 }
293 else
294 {
295 &$discardBCallback( $ai, $bi++, @_ ) while $bi <= $lastB;
296 }
297 }
298
299 # last B?
300 if ( $bi == $lastB + 1 and $ai <= $lastA )
301 {
302 if ( defined($finishedBCallback) )
303 {
304 &$finishedBCallback( $lastB, @_ );
305 $finishedBCallback = undef;
306 }
307 else
308 {
309 &$discardACallback( $ai++, $bi, @_ ) while $ai <= $lastA;
310 }
311 }
312
313 &$discardACallback( $ai++, $bi, @_ ) if $ai <= $lastA;
314 &$discardBCallback( $ai, $bi++, @_ ) if $bi <= $lastB;
315 }
316
317 return 1;
318}
319
320sub traverse_balanced
321{
322 my $a = shift; # array ref
323 my $b = shift; # array ref
324 my $callbacks = shift || {};
325 my $keyGen = shift;
326 my $matchCallback = $callbacks->{'MATCH'} || sub { };
327 my $discardACallback = $callbacks->{'DISCARD_A'} || sub { };
328 my $discardBCallback = $callbacks->{'DISCARD_B'} || sub { };
329 my $changeCallback = $callbacks->{'CHANGE'};
330 my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ );
331
332 # Process all the lines in match vector
333 my $lastA = $#$a;
334 my $lastB = $#$b;
335 my $bi = 0;
336 my $ai = 0;
337 my $ma = -1;
338 my $mb;
339
340 while (1)
341 {
342
343 # Find next match indices $ma and $mb
344 do {
345 $ma++;
346 } while(
347 $ma <= $#$matchVector
348 && !defined $matchVector->[$ma]
349 );
350
351 last if $ma > $#$matchVector; # end of matchVector?
352 $mb = $matchVector->[$ma];
353
354 # Proceed with discard a/b or change events until
355 # next match
356 while ( $ai < $ma || $bi < $mb )
357 {
358
359 if ( $ai < $ma && $bi < $mb )
360 {
361
362 # Change
363 if ( defined $changeCallback )
364 {
365 &$changeCallback( $ai++, $bi++, @_ );
366 }
367 else
368 {
369 &$discardACallback( $ai++, $bi, @_ );
370 &$discardBCallback( $ai, $bi++, @_ );
371 }
372 }
373 elsif ( $ai < $ma )
374 {
375 &$discardACallback( $ai++, $bi, @_ );
376 }
377 else
378 {
379
380 # $bi < $mb
381 &$discardBCallback( $ai, $bi++, @_ );
382 }
383 }
384
385 # Match
386 &$matchCallback( $ai++, $bi++, @_ );
387 }
388
389 while ( $ai <= $lastA || $bi <= $lastB )
390 {
391 if ( $ai <= $lastA && $bi <= $lastB )
392 {
393
394 # Change
395 if ( defined $changeCallback )
396 {
397 &$changeCallback( $ai++, $bi++, @_ );
398 }
399 else
400 {
401 &$discardACallback( $ai++, $bi, @_ );
402 &$discardBCallback( $ai, $bi++, @_ );
403 }
404 }
405 elsif ( $ai <= $lastA )
406 {
407 &$discardACallback( $ai++, $bi, @_ );
408 }
409 else
410 {
411
412 # $bi <= $lastB
413 &$discardBCallback( $ai, $bi++, @_ );
414 }
415 }
416
417 return 1;
418}
419
420sub prepare
421{
422 my $a = shift; # array ref
423 my $keyGen = shift; # code ref
424
425 # set up code ref
426 $keyGen = sub { $_[0] } unless defined($keyGen);
427
428 return scalar _withPositionsOfInInterval( $a, 0, $#$a, $keyGen, @_ );
429}
430
431sub LCS
432{
433 my $a = shift; # array ref
434 my $b = shift; # array ref or hash ref
435 my $matchVector = _longestCommonSubsequence( $a, $b, 0, @_ );
436 my @retval;
437 my $i;
438 for ( $i = 0 ; $i <= $#$matchVector ; $i++ )
439 {
440 if ( defined( $matchVector->[$i] ) )
441 {
442 push ( @retval, $a->[$i] );
443 }
444 }
445 return wantarray ? @retval : \@retval;
446}
447
448sub LCS_length
449{
450 my $a = shift; # array ref
451 my $b = shift; # array ref or hash ref
452 return _longestCommonSubsequence( $a, $b, 1, @_ );
453}
454
455sub LCSidx
456{
457 my $a= shift @_;
458 my $b= shift @_;
459 my $match= _longestCommonSubsequence( $a, $b, 0, @_ );
460 my @am= grep defined $match->[$_], 0..$#$match;
461 my @bm= @{$match}[@am];
462 return \@am, \@bm;
463}
464
465sub compact_diff
466{
467 my $a= shift @_;
468 my $b= shift @_;
469 my( $am, $bm )= LCSidx( $a, $b, @_ );
470 my @cdiff;
471 my( $ai, $bi )= ( 0, 0 );
472 push @cdiff, $ai, $bi;
473 while( 1 ) {
474 while( @$am && $ai == $am->[0] && $bi == $bm->[0] ) {
475 shift @$am;
476 shift @$bm;
477 ++$ai, ++$bi;
478 }
479 push @cdiff, $ai, $bi;
480 last if ! @$am;
481 $ai = $am->[0];
482 $bi = $bm->[0];
483 push @cdiff, $ai, $bi;
484 }
485 push @cdiff, 0+@$a, 0+@$b
486 if $ai < @$a || $bi < @$b;
487 return wantarray ? @cdiff : \@cdiff;
488}
489
490sub diff
491{
492 my $a = shift; # array ref
493 my $b = shift; # array ref
494 my $retval = [];
495 my $hunk = [];
496 my $discard = sub {
497 push @$hunk, [ '-', $_[0], $a->[ $_[0] ] ];
498 };
499 my $add = sub {
500 push @$hunk, [ '+', $_[1], $b->[ $_[1] ] ];
501 };
502 my $match = sub {
503 push @$retval, $hunk
504 if 0 < @$hunk;
505 $hunk = []
506 };
507 traverse_sequences( $a, $b,
508 { MATCH => $match, DISCARD_A => $discard, DISCARD_B => $add }, @_ );
509 &$match();
510 return wantarray ? @$retval : $retval;
511}
512
513sub sdiff
514{
515 my $a = shift; # array ref
516 my $b = shift; # array ref
517 my $retval = [];
518 my $discard = sub { push ( @$retval, [ '-', $a->[ $_[0] ], "" ] ) };
519 my $add = sub { push ( @$retval, [ '+', "", $b->[ $_[1] ] ] ) };
520 my $change = sub {
521 push ( @$retval, [ 'c', $a->[ $_[0] ], $b->[ $_[1] ] ] );
522 };
523 my $match = sub {
524 push ( @$retval, [ 'u', $a->[ $_[0] ], $b->[ $_[1] ] ] );
525 };
526 traverse_balanced(
527 $a,
528 $b,
529 {
530 MATCH => $match,
531 DISCARD_A => $discard,
532 DISCARD_B => $add,
533 CHANGE => $change,
534 },
535 @_
536 );
537 return wantarray ? @$retval : $retval;
538}
539
540########################################
541my $Root= __PACKAGE__;
542package Algorithm::Diff::_impl;
543use strict;
544
545sub _Idx() { 0 } # $me->[_Idx]: Ref to array of hunk indices
546 # 1 # $me->[1]: Ref to first sequence
547 # 2 # $me->[2]: Ref to second sequence
548sub _End() { 3 } # $me->[_End]: Diff between forward and reverse pos
549sub _Same() { 4 } # $me->[_Same]: 1 if pos 1 contains unchanged items
550sub _Base() { 5 } # $me->[_Base]: Added to range's min and max
551sub _Pos() { 6 } # $me->[_Pos]: Which hunk is currently selected
552sub _Off() { 7 } # $me->[_Off]: Offset into _Idx for current position
553sub _Min() { -2 } # Added to _Off to get min instead of max+1
554
555sub Die
556{
557 require Carp;
558 Carp::confess( @_ );
559}
560
561sub _ChkPos
562{
563 my( $me )= @_;
564 return if $me->[_Pos];
565 my $meth= ( caller(1) )[3];
566 Die( "Called $meth on 'reset' object" );
567}
568
569sub _ChkSeq
570{
571 my( $me, $seq )= @_;
572 return $seq + $me->[_Off]
573 if 1 == $seq || 2 == $seq;
574 my $meth= ( caller(1) )[3];
575 Die( "$meth: Invalid sequence number ($seq); must be 1 or 2" );
576}
577
578sub getObjPkg
579{
580 my( $us )= @_;
581 return ref $us if ref $us;
582 return $us . "::_obj";
583}
584
585sub new
586{
587 my( $us, $seq1, $seq2, $opts ) = @_;
588 my @args;
589 for( $opts->{keyGen} ) {
590 push @args, $_ if $_;
591 }
592 for( $opts->{keyGenArgs} ) {
593 push @args, @$_ if $_;
594 }
595 my $cdif= Algorithm::Diff::compact_diff( $seq1, $seq2, @args );
596 my $same= 1;
597 if( 0 == $cdif->[2] && 0 == $cdif->[3] ) {
598 $same= 0;
599 splice @$cdif, 0, 2;
600 }
601 my @obj= ( $cdif, $seq1, $seq2 );
602 $obj[_End] = (1+@$cdif)/2;
603 $obj[_Same] = $same;
604 $obj[_Base] = 0;
605 my $me = bless \@obj, $us->getObjPkg();
606 $me->Reset( 0 );
607 return $me;
608}
609
610sub Reset
611{
612 my( $me, $pos )= @_;
613 $pos= int( $pos || 0 );
614 $pos += $me->[_End]
615 if $pos < 0;
616 $pos= 0
617 if $pos < 0 || $me->[_End] <= $pos;
618 $me->[_Pos]= $pos || !1;
619 $me->[_Off]= 2*$pos - 1;
620 return $me;
621}
622
623sub Base
624{
625 my( $me, $base )= @_;
626 my $oldBase= $me->[_Base];
627 $me->[_Base]= 0+$base if defined $base;
628 return $oldBase;
629}
630
631sub Copy
632{
633 my( $me, $pos, $base )= @_;
634 my @obj= @$me;
635 my $you= bless \@obj, ref($me);
636 $you->Reset( $pos ) if defined $pos;
637 $you->Base( $base );
638 return $you;
639}
640
641sub Next {
642 my( $me, $steps )= @_;
643 $steps= 1 if ! defined $steps;
644 if( $steps ) {
645 my $pos= $me->[_Pos];
646 my $new= $pos + $steps;
647 $new= 0 if $pos && $new < 0;
648 $me->Reset( $new )
649 }
650 return $me->[_Pos];
651}
652
653sub Prev {
654 my( $me, $steps )= @_;
655 $steps= 1 if ! defined $steps;
656 my $pos= $me->Next(-$steps);
657 $pos -= $me->[_End] if $pos;
658 return $pos;
659}
660
661sub Diff {
662 my( $me )= @_;
663 $me->_ChkPos();
664 return 0 if $me->[_Same] == ( 1 & $me->[_Pos] );
665 my $ret= 0;
666 my $off= $me->[_Off];
667 for my $seq ( 1, 2 ) {
668 $ret |= $seq
669 if $me->[_Idx][ $off + $seq + _Min ]
670 < $me->[_Idx][ $off + $seq ];
671 }
672 return $ret;
673}
674
675sub Min {
676 my( $me, $seq, $base )= @_;
677 $me->_ChkPos();
678 my $off= $me->_ChkSeq($seq);
679 $base= $me->[_Base] if !defined $base;
680 return $base + $me->[_Idx][ $off + _Min ];
681}
682
683sub Max {
684 my( $me, $seq, $base )= @_;
685 $me->_ChkPos();
686 my $off= $me->_ChkSeq($seq);
687 $base= $me->[_Base] if !defined $base;
688 return $base + $me->[_Idx][ $off ] -1;
689}
690
691sub Range {
692 my( $me, $seq, $base )= @_;
693 $me->_ChkPos();
694 my $off = $me->_ChkSeq($seq);
695 if( !wantarray ) {
696 return $me->[_Idx][ $off ]
697 - $me->[_Idx][ $off + _Min ];
698 }
699 $base= $me->[_Base] if !defined $base;
700 return ( $base + $me->[_Idx][ $off + _Min ] )
701 .. ( $base + $me->[_Idx][ $off ] - 1 );
702}
703
704sub Items {
705 my( $me, $seq )= @_;
706 $me->_ChkPos();
707 my $off = $me->_ChkSeq($seq);
708 if( !wantarray ) {
709 return $me->[_Idx][ $off ]
710 - $me->[_Idx][ $off + _Min ];
711 }
712 return
713 @{$me->[$seq]}[
714 $me->[_Idx][ $off + _Min ]
715 .. ( $me->[_Idx][ $off ] - 1 )
716 ];
717}
718
719sub Same {
720 my( $me )= @_;
721 $me->_ChkPos();
722 return wantarray ? () : 0
723 if $me->[_Same] != ( 1 & $me->[_Pos] );
724 return $me->Items(1);
725}
726
727my %getName;
728BEGIN {
729 %getName= (
730 same => \&Same,
731 diff => \&Diff,
732 base => \&Base,
733 min => \&Min,
734 max => \&Max,
735 range=> \&Range,
736 items=> \&Items, # same thing
737 );
738}
739
740sub Get
741{
742 my $me= shift @_;
743 $me->_ChkPos();
744 my @value;
745 for my $arg ( @_ ) {
746 for my $word ( split ' ', $arg ) {
747 my $meth;
748 if( $word !~ /^(-?\d+)?([a-zA-Z]+)([12])?$/
749 || not $meth= $getName{ lc $2 }
750 ) {
751 Die( $Root, ", Get: Invalid request ($word)" );
752 }
753 my( $base, $name, $seq )= ( $1, $2, $3 );
754 push @value, scalar(
755 4 == length($name)
756 ? $meth->( $me )
757 : $meth->( $me, $seq, $base )
758 );
759 }
760 }
761 if( wantarray ) {
762 return @value;
763 } elsif( 1 == @value ) {
764 return $value[0];
765 }
766 Die( 0+@value, " values requested from ",
767 $Root, "'s Get in scalar context" );
768}
769
770
771my $Obj= getObjPkg($Root);
772no strict 'refs';
773
774for my $meth ( qw( new getObjPkg ) ) {
775 *{$Root."::".$meth} = \&{$meth};
776 *{$Obj ."::".$meth} = \&{$meth};
777}
778for my $meth ( qw(
779 Next Prev Reset Copy Base Diff
780 Same Items Range Min Max Get
781 _ChkPos _ChkSeq
782) ) {
783 *{$Obj."::".$meth} = \&{$meth};
784}
785
7861;
787__END__
788
789=head1 NAME
790
791Algorithm::Diff - Compute `intelligent' differences between two files / lists
792
793=head1 SYNOPSIS
794
795 require Algorithm::Diff;
796
797 # This example produces traditional 'diff' output:
798
799 my $diff = Algorithm::Diff->new( \@seq1, \@seq2 );
800
801 $diff->Base( 1 ); # Return line numbers, not indices
802 while( $diff->Next() ) {
803 next if $diff->Same();
804 my $sep = '';
805 if( ! $diff->Items(2) ) {
806 sprintf "%d,%dd%d\n",
807 $diff->Get(qw( Min1 Max1 Max2 ));
808 } elsif( ! $diff->Items(1) ) {
809 sprint "%da%d,%d\n",
810 $diff->Get(qw( Max1 Min2 Max2 ));
811 } else {
812 $sep = "---\n";
813 sprintf "%d,%dc%d,%d\n",
814 $diff->Get(qw( Min1 Max1 Min2 Max2 ));
815 }
816 print "< $_" for $diff->Items(1);
817 print $sep;
818 print "> $_" for $diff->Items(2);
819 }
820
821
822 # Alternate interfaces:
823
824 use Algorithm::Diff qw(
825 LCS LCS_length LCSidx
826 diff sdiff compact_diff
827 traverse_sequences traverse_balanced );
828
829 @lcs = LCS( \@seq1, \@seq2 );
830 $lcsref = LCS( \@seq1, \@seq2 );
831 $count = LCS_length( \@seq1, \@seq2 );
832
833 ( $seq1idxref, $seq2idxref ) = LCSidx( \@seq1, \@seq2 );
834
835
836 # Complicated interfaces:
837
838 @diffs = diff( \@seq1, \@seq2 );
839
840 @sdiffs = sdiff( \@seq1, \@seq2 );
841
842 @cdiffs = compact_diff( \@seq1, \@seq2 );
843
844 traverse_sequences(
845 \@seq1,
846 \@seq2,
847 { MATCH => \&callback1,
848 DISCARD_A => \&callback2,
849 DISCARD_B => \&callback3,
850 },
851 \&key_generator,
852 @extra_args,
853 );
854
855 traverse_balanced(
856 \@seq1,
857 \@seq2,
858 { MATCH => \&callback1,
859 DISCARD_A => \&callback2,
860 DISCARD_B => \&callback3,
861 CHANGE => \&callback4,
862 },
863 \&key_generator,
864 @extra_args,
865 );
866
867
868=head1 INTRODUCTION
869
870(by Mark-Jason Dominus)
871
872I once read an article written by the authors of C<diff>; they said
873that they worked very hard on the algorithm until they found the
874right one.
875
876I think what they ended up using (and I hope someone will correct me,
877because I am not very confident about this) was the `longest common
878subsequence' method. In the LCS problem, you have two sequences of
879items:
880
881 a b c d f g h j q z
882
883 a b c d e f g i j k r x y z
884
885and you want to find the longest sequence of items that is present in
886both original sequences in the same order. That is, you want to find
887a new sequence I<S> which can be obtained from the first sequence by
888deleting some items, and from the secend sequence by deleting other
889items. You also want I<S> to be as long as possible. In this case I<S>
890is
891
892 a b c d f g j z
893
894From there it's only a small step to get diff-like output:
895
896 e h i k q r x y
897 + - + + - + + +
898
899This module solves the LCS problem. It also includes a canned function
900to generate C<diff>-like output.
901
902It might seem from the example above that the LCS of two sequences is
903always pretty obvious, but that's not always the case, especially when
904the two sequences have many repeated elements. For example, consider
905
906 a x b y c z p d q
907 a b c a x b y c z
908
909A naive approach might start by matching up the C<a> and C<b> that
910appear at the beginning of each sequence, like this:
911
912 a x b y c z p d q
913 a b c a b y c z
914
915This finds the common subsequence C<a b c z>. But actually, the LCS
916is C<a x b y c z>:
917
918 a x b y c z p d q
919 a b c a x b y c z
920
921or
922
923 a x b y c z p d q
924 a b c a x b y c z
925
926=head1 USAGE
927
928(See also the README file and several example
929scripts include with this module.)
930
931This module now provides an object-oriented interface that uses less
932memory and is easier to use than most of the previous procedural
933interfaces. It also still provides several exportable functions. We'll
934deal with these in ascending order of difficulty: C<LCS>,
935C<LCS_length>, C<LCSidx>, OO interface, C<prepare>, C<diff>, C<sdiff>,
936C<traverse_sequences>, and C<traverse_balanced>.
937
938=head2 C<LCS>
939
940Given references to two lists of items, LCS returns an array containing
941their longest common subsequence. In scalar context, it returns a
942reference to such a list.
943
944 @lcs = LCS( \@seq1, \@seq2 );
945 $lcsref = LCS( \@seq1, \@seq2 );
946
947C<LCS> may be passed an optional third parameter; this is a CODE
948reference to a key generation function. See L</KEY GENERATION
949FUNCTIONS>.
950
951 @lcs = LCS( \@seq1, \@seq2, \&keyGen, @args );
952 $lcsref = LCS( \@seq1, \@seq2, \&keyGen, @args );
953
954Additional parameters, if any, will be passed to the key generation
955routine.
956
957=head2 C<LCS_length>
958
959This is just like C<LCS> except it only returns the length of the
960longest common subsequence. This provides a performance gain of about
9619% compared to C<LCS>.
962
963=head2 C<LCSidx>
964
965Like C<LCS> except it returns references to two arrays. The first array
966contains the indices into @seq1 where the LCS items are located. The
967second array contains the indices into @seq2 where the LCS items are located.
968
969Therefore, the following three lists will contain the same values:
970
971 my( $idx1, $idx2 ) = LCSidx( \@seq1, \@seq2 );
972 my @list1 = @seq1[ @$idx1 ];
973 my @list2 = @seq2[ @$idx2 ];
974 my @list3 = LCS( \@seq1, \@seq2 );
975
976=head2 C<new>
977
978 $diff = Algorithm::Diffs->new( \@seq1, \@seq2 );
979 $diff = Algorithm::Diffs->new( \@seq1, \@seq2, \%opts );
980
981C<new> computes the smallest set of additions and deletions necessary
982to turn the first sequence into the second and compactly records them
983in the object.
984
985You use the object to iterate over I<hunks>, where each hunk represents
986a contiguous section of items which should be added, deleted, replaced,
987or left unchanged.
988
989=over 4
990
991The following summary of all of the methods looks a lot like Perl code
992but some of the symbols have different meanings:
993
994 [ ] Encloses optional arguments
995 : Is followed by the default value for an optional argument
996 | Separates alternate return results
997
998Method summary:
999
1000 $obj = Algorithm::Diff->new( \@seq1, \@seq2, [ \%opts ] );
1001 $pos = $obj->Next( [ $count : 1 ] );
1002 $revPos = $obj->Prev( [ $count : 1 ] );
1003 $obj = $obj->Reset( [ $pos : 0 ] );
1004 $copy = $obj->Copy( [ $pos, [ $newBase ] ] );
1005 $oldBase = $obj->Base( [ $newBase ] );
1006
1007Note that all of the following methods C<die> if used on an object that
1008is "reset" (not currently pointing at any hunk).
1009
1010 $bits = $obj->Diff( );
1011 @items|$cnt = $obj->Same( );
1012 @items|$cnt = $obj->Items( $seqNum );
1013 @idxs |$cnt = $obj->Range( $seqNum, [ $base ] );
1014 $minIdx = $obj->Min( $seqNum, [ $base ] );
1015 $maxIdx = $obj->Max( $seqNum, [ $base ] );
1016 @values = $obj->Get( @names );
1017
1018Passing in C<undef> for an optional argument is always treated the same
1019as if no argument were passed in.
1020
1021=item C<Next>
1022
1023 $pos = $diff->Next(); # Move forward 1 hunk
1024 $pos = $diff->Next( 2 ); # Move forward 2 hunks
1025 $pos = $diff->Next(-5); # Move backward 5 hunks
1026
1027C<Next> moves the object to point at the next hunk. The object starts
1028out "reset", which means it isn't pointing at any hunk. If the object
1029is reset, then C<Next()> moves to the first hunk.
1030
1031C<Next> returns a true value iff the move didn't go past the last hunk.
1032So C<Next(0)> will return true iff the object is not reset.
1033
1034Actually, C<Next> returns the object's new position, which is a number
1035between 1 and the number of hunks (inclusive), or returns a false value.
1036
1037=item C<Prev>
1038
1039C<Prev($N)> is almost identical to C<Next(-$N)>; it moves to the $Nth
1040previous hunk. On a 'reset' object, C<Prev()> [and C<Next(-1)>] move
1041to the last hunk.
1042
1043The position returned by C<Prev> is relative to the I<end> of the
1044hunks; -1 for the last hunk, -2 for the second-to-last, etc.
1045
1046=item C<Reset>
1047
1048 $diff->Reset(); # Reset the object's position
1049 $diff->Reset($pos); # Move to the specified hunk
1050 $diff->Reset(1); # Move to the first hunk
1051 $diff->Reset(-1); # Move to the last hunk
1052
1053C<Reset> returns the object, so, for example, you could use
1054C<< $diff->Reset()->Next(-1) >> to get the number of hunks.
1055
1056=item C<Copy>
1057
1058 $copy = $diff->Copy( $newPos, $newBase );
1059
1060C<Copy> returns a copy of the object. The copy and the orignal object
1061share most of their data, so making copies takes very little memory.
1062The copy maintains its own position (separate from the original), which
1063is the main purpose of copies. It also maintains its own base.
1064
1065By default, the copy's position starts out the same as the original
1066object's position. But C<Copy> takes an optional first argument to set the
1067new position, so the following three snippets are equivalent:
1068
1069 $copy = $diff->Copy($pos);
1070
1071 $copy = $diff->Copy();
1072 $copy->Reset($pos);
1073
1074 $copy = $diff->Copy()->Reset($pos);
1075
1076C<Copy> takes an optional second argument to set the base for
1077the copy. If you wish to change the base of the copy but leave
1078the position the same as in the original, here are two
1079equivalent ways:
1080
1081 $copy = $diff->Copy();
1082 $copy->Base( 0 );
1083
1084 $copy = $diff->Copy(undef,0);
1085
1086Here are two equivalent way to get a "reset" copy:
1087
1088 $copy = $diff->Copy(0);
1089
1090 $copy = $diff->Copy()->Reset();
1091
1092=item C<Diff>
1093
1094 $bits = $obj->Diff();
1095
1096C<Diff> returns a true value iff the current hunk contains items that are
1097different between the two sequences. It actually returns one of the
1098follow 4 values:
1099
1100=over 4
1101
1102=item 3
1103
1104C<3==(1|2)>. This hunk contains items from @seq1 and the items
1105from @seq2 that should replace them. Both sequence 1 and 2
1106contain changed items so both the 1 and 2 bits are set.
1107
1108=item 2
1109
1110This hunk only contains items from @seq2 that should be inserted (not
1111items from @seq1). Only sequence 2 contains changed items so only the 2
1112bit is set.
1113
1114=item 1
1115
1116This hunk only contains items from @seq1 that should be deleted (not
1117items from @seq2). Only sequence 1 contains changed items so only the 1
1118bit is set.
1119
1120=item 0
1121
1122This means that the items in this hunk are the same in both sequences.
1123Neither sequence 1 nor 2 contain changed items so neither the 1 nor the
11242 bits are set.
1125
1126=back
1127
1128=item C<Same>
1129
1130C<Same> returns a true value iff the current hunk contains items that
1131are the same in both sequences. It actually returns the list of items
1132if they are the same or an emty list if they aren't. In a scalar
1133context, it returns the size of the list.
1134
1135=item C<Items>
1136
1137 $count = $diff->Items(2);
1138 @items = $diff->Items($seqNum);
1139
1140C<Items> returns the (number of) items from the specified sequence that
1141are part of the current hunk.
1142
1143If the current hunk contains only insertions, then
1144C<< $diff->Items(1) >> will return an empty list (0 in a scalar conext).
1145If the current hunk contains only deletions, then C<< $diff->Items(2) >>
1146will return an empty list (0 in a scalar conext).
1147
1148If the hunk contains replacements, then both C<< $diff->Items(1) >> and
1149C<< $diff->Items(2) >> will return different, non-empty lists.
1150
1151Otherwise, the hunk contains identical items and all of the following
1152will return the same lists:
1153
1154 @items = $diff->Items(1);
1155 @items = $diff->Items(2);
1156 @items = $diff->Same();
1157
1158=item C<Range>
1159
1160 $count = $diff->Range( $seqNum );
1161 @indices = $diff->Range( $seqNum );
1162 @indices = $diff->Range( $seqNum, $base );
1163
1164C<Range> is like C<Items> except that it returns a list of I<indices> to
1165the items rather than the items themselves. By default, the index of
1166the first item (in each sequence) is 0 but this can be changed by
1167calling the C<Base> method. So, by default, the following two snippets
1168return the same lists:
1169
1170 @list = $diff->Items(2);
1171 @list = @seq2[ $diff->Range(2) ];
1172
1173You can also specify the base to use as the second argument. So the
1174following two snippets I<always> return the same lists:
1175
1176 @list = $diff->Items(1);
1177 @list = @seq1[ $diff->Range(1,0) ];
1178
1179=item C<Base>
1180
1181 $curBase = $diff->Base();
1182 $oldBase = $diff->Base($newBase);
1183
1184C<Base> sets and/or returns the current base (usually 0 or 1) that is
1185used when you request range information. The base defaults to 0 so
1186that range information is returned as array indices. You can set the
1187base to 1 if you want to report traditional line numbers instead.
1188
1189=item C<Min>
1190
1191 $min1 = $diff->Min(1);
1192 $min = $diff->Min( $seqNum, $base );
1193
1194C<Min> returns the first value that C<Range> would return (given the
1195same arguments) or returns C<undef> if C<Range> would return an empty
1196list.
1197
1198=item C<Max>
1199
1200C<Max> returns the last value that C<Range> would return or C<undef>.
1201
1202=item C<Get>
1203
1204 ( $n, $x, $r ) = $diff->Get(qw( min1 max1 range1 ));
1205 @values = $diff->Get(qw( 0min2 1max2 range2 same base ));
1206
1207C<Get> returns one or more scalar values. You pass in a list of the
1208names of the values you want returned. Each name must match one of the
1209following regexes:
1210
1211 /^(-?\d+)?(min|max)[12]$/i
1212 /^(range[12]|same|diff|base)$/i
1213
1214The 1 or 2 after a name says which sequence you want the information
1215for (and where allowed, it is required). The optional number before
1216"min" or "max" is the base to use. So the following equalities hold:
1217
1218 $diff->Get('min1') == $diff->Min(1)
1219 $diff->Get('0min2') == $diff->Min(2,0)
1220
1221Using C<Get> in a scalar context when you've passed in more than one
1222name is a fatal error (C<die> is called).
1223
1224=back
1225
1226=head2 C<prepare>
1227
1228Given a reference to a list of items, C<prepare> returns a reference
1229to a hash which can be used when comparing this sequence to other
1230sequences with C<LCS> or C<LCS_length>.
1231
1232 $prep = prepare( \@seq1 );
1233 for $i ( 0 .. 10_000 )
1234 {
1235 @lcs = LCS( $prep, $seq[$i] );
1236 # do something useful with @lcs
1237 }
1238
1239C<prepare> may be passed an optional third parameter; this is a CODE
1240reference to a key generation function. See L</KEY GENERATION
1241FUNCTIONS>.
1242
1243 $prep = prepare( \@seq1, \&keyGen );
1244 for $i ( 0 .. 10_000 )
1245 {
1246 @lcs = LCS( $seq[$i], $prep, \&keyGen );
1247 # do something useful with @lcs
1248 }
1249
1250Using C<prepare> provides a performance gain of about 50% when calling LCS
1251many times compared with not preparing.
1252
1253=head2 C<diff>
1254
1255 @diffs = diff( \@seq1, \@seq2 );
1256 $diffs_ref = diff( \@seq1, \@seq2 );
1257
1258C<diff> computes the smallest set of additions and deletions necessary
1259to turn the first sequence into the second, and returns a description
1260of these changes. The description is a list of I<hunks>; each hunk
1261represents a contiguous section of items which should be added,
1262deleted, or replaced. (Hunks containing unchanged items are not
1263included.)
1264
1265The return value of C<diff> is a list of hunks, or, in scalar context, a
1266reference to such a list. If there are no differences, the list will be
1267empty.
1268
1269Here is an example. Calling C<diff> for the following two sequences:
1270
1271 a b c e h j l m n p
1272 b c d e f j k l m r s t
1273
1274would produce the following list:
1275
1276 (
1277 [ [ '-', 0, 'a' ] ],
1278
1279 [ [ '+', 2, 'd' ] ],
1280
1281 [ [ '-', 4, 'h' ],
1282 [ '+', 4, 'f' ] ],
1283
1284 [ [ '+', 6, 'k' ] ],
1285
1286 [ [ '-', 8, 'n' ],
1287 [ '-', 9, 'p' ],
1288 [ '+', 9, 'r' ],
1289 [ '+', 10, 's' ],
1290 [ '+', 11, 't' ] ],
1291 )
1292
1293There are five hunks here. The first hunk says that the C<a> at
1294position 0 of the first sequence should be deleted (C<->). The second
1295hunk says that the C<d> at position 2 of the second sequence should
1296be inserted (C<+>). The third hunk says that the C<h> at position 4
1297of the first sequence should be removed and replaced with the C<f>
1298from position 4 of the second sequence. And so on.
1299
1300C<diff> may be passed an optional third parameter; this is a CODE
1301reference to a key generation function. See L</KEY GENERATION
1302FUNCTIONS>.
1303
1304Additional parameters, if any, will be passed to the key generation
1305routine.
1306
1307=head2 C<sdiff>
1308
1309 @sdiffs = sdiff( \@seq1, \@seq2 );
1310 $sdiffs_ref = sdiff( \@seq1, \@seq2 );
1311
1312C<sdiff> computes all necessary components to show two sequences
1313and their minimized differences side by side, just like the
1314Unix-utility I<sdiff> does:
1315
1316 same same
1317 before | after
1318 old < -
1319 - > new
1320
1321It returns a list of array refs, each pointing to an array of
1322display instructions. In scalar context it returns a reference
1323to such a list. If there are no differences, the list will have one
1324entry per item, each indicating that the item was unchanged.
1325
1326Display instructions consist of three elements: A modifier indicator
1327(C<+>: Element added, C<->: Element removed, C<u>: Element unmodified,
1328C<c>: Element changed) and the value of the old and new elements, to
1329be displayed side-by-side.
1330
1331An C<sdiff> of the following two sequences:
1332
1333 a b c e h j l m n p
1334 b c d e f j k l m r s t
1335
1336results in
1337
1338 ( [ '-', 'a', '' ],
1339 [ 'u', 'b', 'b' ],
1340 [ 'u', 'c', 'c' ],
1341 [ '+', '', 'd' ],
1342 [ 'u', 'e', 'e' ],
1343 [ 'c', 'h', 'f' ],
1344 [ 'u', 'j', 'j' ],
1345 [ '+', '', 'k' ],
1346 [ 'u', 'l', 'l' ],
1347 [ 'u', 'm', 'm' ],
1348 [ 'c', 'n', 'r' ],
1349 [ 'c', 'p', 's' ],
1350 [ '+', '', 't' ],
1351 )
1352
1353C<sdiff> may be passed an optional third parameter; this is a CODE
1354reference to a key generation function. See L</KEY GENERATION
1355FUNCTIONS>.
1356
1357Additional parameters, if any, will be passed to the key generation
1358routine.
1359
1360=head2 C<compact_diff>
1361
1362C<compact_diff> is much like C<sdiff> except it returns a much more
1363compact description consisting of just one flat list of indices. An
1364example helps explain the format:
1365
1366 my @a = qw( a b c e h j l m n p );
1367 my @b = qw( b c d e f j k l m r s t );
1368 @cdiff = compact_diff( \@a, \@b );
1369 # Returns:
1370 # @a @b @a @b
1371 # start start values values
1372 ( 0, 0, # =
1373 0, 0, # a !
1374 1, 0, # b c = b c
1375 3, 2, # ! d
1376 3, 3, # e = e
1377 4, 4, # f ! h
1378 5, 5, # j = j
1379 6, 6, # ! k
1380 6, 7, # l m = l m
1381 8, 9, # n p ! r s t
1382 10, 12, #
1383 );
1384
1385The 0th, 2nd, 4th, etc. entries are all indices into @seq1 (@a in the
1386above example) indicating where a hunk begins. The 1st, 3rd, 5th, etc.
1387entries are all indices into @seq2 (@b in the above example) indicating
1388where the same hunk begins.
1389
1390So each pair of indices (except the last pair) describes where a hunk
1391begins (in each sequence). Since each hunk must end at the item just
1392before the item that starts the next hunk, the next pair of indices can
1393be used to determine where the hunk ends.
1394
1395So, the first 4 entries (0..3) describe the first hunk. Entries 0 and 1
1396describe where the first hunk begins (and so are always both 0).
1397Entries 2 and 3 describe where the next hunk begins, so subtracting 1
1398from each tells us where the first hunk ends. That is, the first hunk
1399contains items C<$diff[0]> through C<$diff[2] - 1> of the first sequence
1400and contains items C<$diff[1]> through C<$diff[3] - 1> of the second
1401sequence.
1402
1403In other words, the first hunk consists of the following two lists of items:
1404
1405 # 1st pair 2nd pair
1406 # of indices of indices
1407 @list1 = @a[ $cdiff[0] .. $cdiff[2]-1 ];
1408 @list2 = @b[ $cdiff[1] .. $cdiff[3]-1 ];
1409 # Hunk start Hunk end
1410
1411Note that the hunks will always alternate between those that are part of
1412the LCS (those that contain unchanged items) and those that contain
1413changes. This means that all we need to be told is whether the first
1414hunk is a 'same' or 'diff' hunk and we can determine which of the other
1415hunks contain 'same' items or 'diff' items.
1416
1417By convention, we always make the first hunk contain unchanged items.
1418So the 1st, 3rd, 5th, etc. hunks (all odd-numbered hunks if you start
1419counting from 1) all contain unchanged items. And the 2nd, 4th, 6th,
1420etc. hunks (all even-numbered hunks if you start counting from 1) all
1421contain changed items.
1422
1423Since @a and @b don't begin with the same value, the first hunk in our
1424example is empty (otherwise we'd violate the above convention). Note
1425that the first 4 index values in our example are all zero. Plug these
1426values into our previous code block and we get:
1427
1428 @hunk1a = @a[ 0 .. 0-1 ];
1429 @hunk1b = @b[ 0 .. 0-1 ];
1430
1431And C<0..-1> returns the empty list.
1432
1433Move down one pair of indices (2..5) and we get the offset ranges for
1434the second hunk, which contains changed items.
1435
1436Since C<@diff[2..5]> contains (0,0,1,0) in our example, the second hunk
1437consists of these two lists of items:
1438
1439 @hunk2a = @a[ $cdiff[2] .. $cdiff[4]-1 ];
1440 @hunk2b = @b[ $cdiff[3] .. $cdiff[5]-1 ];
1441 # or
1442 @hunk2a = @a[ 0 .. 1-1 ];
1443 @hunk2b = @b[ 0 .. 0-1 ];
1444 # or
1445 @hunk2a = @a[ 0 .. 0 ];
1446 @hunk2b = @b[ 0 .. -1 ];
1447 # or
1448 @hunk2a = ( 'a' );
1449 @hunk2b = ( );
1450
1451That is, we would delete item 0 ('a') from @a.
1452
1453Since C<@diff[4..7]> contains (1,0,3,2) in our example, the third hunk
1454consists of these two lists of items:
1455
1456 @hunk3a = @a[ $cdiff[4] .. $cdiff[6]-1 ];
1457 @hunk3a = @b[ $cdiff[5] .. $cdiff[7]-1 ];
1458 # or
1459 @hunk3a = @a[ 1 .. 3-1 ];
1460 @hunk3a = @b[ 0 .. 2-1 ];
1461 # or
1462 @hunk3a = @a[ 1 .. 2 ];
1463 @hunk3a = @b[ 0 .. 1 ];
1464 # or
1465 @hunk3a = qw( b c );
1466 @hunk3a = qw( b c );
1467
1468Note that this third hunk contains unchanged items as our convention demands.
1469
1470You can continue this process until you reach the last two indices,
1471which will always be the number of items in each sequence. This is
1472required so that subtracting one from each will give you the indices to
1473the last items in each sequence.
1474
1475=head2 C<traverse_sequences>
1476
1477C<traverse_sequences> used to be the most general facility provided by
1478this module (the new OO interface is more powerful and much easier to
1479use).
1480
1481Imagine that there are two arrows. Arrow A points to an element of
1482sequence A, and arrow B points to an element of the sequence B.
1483Initially, the arrows point to the first elements of the respective
1484sequences. C<traverse_sequences> will advance the arrows through the
1485sequences one element at a time, calling an appropriate user-specified
1486callback function before each advance. It willadvance the arrows in
1487such a way that if there are equal elements C<$A[$i]> and C<$B[$j]>
1488which are equal and which are part of the LCS, there will be some moment
1489during the execution of C<traverse_sequences> when arrow A is pointing
1490to C<$A[$i]> and arrow B is pointing to C<$B[$j]>. When this happens,
1491C<traverse_sequences> will call the C<MATCH> callback function and then
1492it will advance both arrows.
1493
1494Otherwise, one of the arrows is pointing to an element of its sequence
1495that is not part of the LCS. C<traverse_sequences> will advance that
1496arrow and will call the C<DISCARD_A> or the C<DISCARD_B> callback,
1497depending on which arrow it advanced. If both arrows point to elements
1498that are not part of the LCS, then C<traverse_sequences> will advance
1499one of them and call the appropriate callback, but it is not specified
1500which it will call.
1501
1502The arguments to C<traverse_sequences> are the two sequences to
1503traverse, and a hash which specifies the callback functions, like this:
1504
1505 traverse_sequences(
1506 \@seq1, \@seq2,
1507 { MATCH => $callback_1,
1508 DISCARD_A => $callback_2,
1509 DISCARD_B => $callback_3,
1510 }
1511 );
1512
1513Callbacks for MATCH, DISCARD_A, and DISCARD_B are invoked with at least
1514the indices of the two arrows as their arguments. They are not expected
1515to return any values. If a callback is omitted from the table, it is
1516not called.
1517
1518Callbacks for A_FINISHED and B_FINISHED are invoked with at least the
1519corresponding index in A or B.
1520
1521If arrow A reaches the end of its sequence, before arrow B does,
1522C<traverse_sequences> will call the C<A_FINISHED> callback when it
1523advances arrow B, if there is such a function; if not it will call
1524C<DISCARD_B> instead. Similarly if arrow B finishes first.
1525C<traverse_sequences> returns when both arrows are at the ends of their
1526respective sequences. It returns true on success and false on failure.
1527At present there is no way to fail.
1528
1529C<traverse_sequences> may be passed an optional fourth parameter; this
1530is a CODE reference to a key generation function. See L</KEY GENERATION
1531FUNCTIONS>.
1532
1533Additional parameters, if any, will be passed to the key generation function.
1534
1535If you want to pass additional parameters to your callbacks, but don't
1536need a custom key generation function, you can get the default by
1537passing undef:
1538
1539 traverse_sequences(
1540 \@seq1, \@seq2,
1541 { MATCH => $callback_1,
1542 DISCARD_A => $callback_2,
1543 DISCARD_B => $callback_3,
1544 },
1545 undef, # default key-gen
1546 $myArgument1,
1547 $myArgument2,
1548 $myArgument3,
1549 );
1550
1551C<traverse_sequences> does not have a useful return value; you are
1552expected to plug in the appropriate behavior with the callback
1553functions.
1554
1555=head2 C<traverse_balanced>
1556
1557C<traverse_balanced> is an alternative to C<traverse_sequences>. It
1558uses a different algorithm to iterate through the entries in the
1559computed LCS. Instead of sticking to one side and showing element changes
1560as insertions and deletions only, it will jump back and forth between
1561the two sequences and report I<changes> occurring as deletions on one
1562side followed immediatly by an insertion on the other side.
1563
1564In addition to the C<DISCARD_A>, C<DISCARD_B>, and C<MATCH> callbacks
1565supported by C<traverse_sequences>, C<traverse_balanced> supports
1566a C<CHANGE> callback indicating that one element got C<replaced> by another:
1567
1568 traverse_balanced(
1569 \@seq1, \@seq2,
1570 { MATCH => $callback_1,
1571 DISCARD_A => $callback_2,
1572 DISCARD_B => $callback_3,
1573 CHANGE => $callback_4,
1574 }
1575 );
1576
1577If no C<CHANGE> callback is specified, C<traverse_balanced>
1578will map C<CHANGE> events to C<DISCARD_A> and C<DISCARD_B> actions,
1579therefore resulting in a similar behaviour as C<traverse_sequences>
1580with different order of events.
1581
1582C<traverse_balanced> might be a bit slower than C<traverse_sequences>,
1583noticable only while processing huge amounts of data.
1584
1585The C<sdiff> function of this module
1586is implemented as call to C<traverse_balanced>.
1587
1588C<traverse_balanced> does not have a useful return value; you are expected to
1589plug in the appropriate behavior with the callback functions.
1590
1591=head1 KEY GENERATION FUNCTIONS
1592
1593Most of the functions accept an optional extra parameter. This is a
1594CODE reference to a key generating (hashing) function that should return
1595a string that uniquely identifies a given element. It should be the
1596case that if two elements are to be considered equal, their keys should
1597be the same (and the other way around). If no key generation function
1598is provided, the key will be the element as a string.
1599
1600By default, comparisons will use "eq" and elements will be turned into keys
1601using the default stringizing operator '""'.
1602
1603Where this is important is when you're comparing something other than
1604strings. If it is the case that you have multiple different objects
1605that should be considered to be equal, you should supply a key
1606generation function. Otherwise, you have to make sure that your arrays
1607contain unique references.
1608
1609For instance, consider this example:
1610
1611 package Person;
1612
1613 sub new
1614 {
1615 my $package = shift;
1616 return bless { name => '', ssn => '', @_ }, $package;
1617 }
1618
1619 sub clone
1620 {
1621 my $old = shift;
1622 my $new = bless { %$old }, ref($old);
1623 }
1624
1625 sub hash
1626 {
1627 return shift()->{'ssn'};
1628 }
1629
1630 my $person1 = Person->new( name => 'Joe', ssn => '123-45-6789' );
1631 my $person2 = Person->new( name => 'Mary', ssn => '123-47-0000' );
1632 my $person3 = Person->new( name => 'Pete', ssn => '999-45-2222' );
1633 my $person4 = Person->new( name => 'Peggy', ssn => '123-45-9999' );
1634 my $person5 = Person->new( name => 'Frank', ssn => '000-45-9999' );
1635
1636If you did this:
1637
1638 my $array1 = [ $person1, $person2, $person4 ];
1639 my $array2 = [ $person1, $person3, $person4, $person5 ];
1640 Algorithm::Diff::diff( $array1, $array2 );
1641
1642everything would work out OK (each of the objects would be converted
1643into a string like "Person=HASH(0x82425b0)" for comparison).
1644
1645But if you did this:
1646
1647 my $array1 = [ $person1, $person2, $person4 ];
1648 my $array2 = [ $person1, $person3, $person4->clone(), $person5 ];
1649 Algorithm::Diff::diff( $array1, $array2 );
1650
1651$person4 and $person4->clone() (which have the same name and SSN)
1652would be seen as different objects. If you wanted them to be considered
1653equivalent, you would have to pass in a key generation function:
1654
1655 my $array1 = [ $person1, $person2, $person4 ];
1656 my $array2 = [ $person1, $person3, $person4->clone(), $person5 ];
1657 Algorithm::Diff::diff( $array1, $array2, \&Person::hash );
1658
1659This would use the 'ssn' field in each Person as a comparison key, and
1660so would consider $person4 and $person4->clone() as equal.
1661
1662You may also pass additional parameters to the key generation function
1663if you wish.
1664
1665=head1 ERROR CHECKING
1666
1667If you pass these routines a non-reference and they expect a reference,
1668they will die with a message.
1669
1670=head1 AUTHOR
1671
1672This version released by Tye McQueen (http://perlmonks.org/?node=tye).
1673
1674=head1 LICENSE
1675
1676Parts Copyright (c) 2000-2004 Ned Konz. All rights reserved.
1677Parts by Tye McQueen.
1678
1679This program is free software; you can redistribute it and/or modify it
1680under the same terms as Perl.
1681
1682=head1 MAILING LIST
1683
1684Mark-Jason still maintains a mailing list. To join a low-volume mailing
1685list for announcements related to diff and Algorithm::Diff, send an
1686empty mail message to mjd-perl-diff-request@plover.com.
1687
1688=head1 CREDITS
1689
1690Versions through 0.59 (and much of this documentation) were written by:
1691
1692Mark-Jason Dominus, mjd-perl-diff@plover.com
1693
1694This version borrows some documentation and routine names from
1695Mark-Jason's, but Diff.pm's code was completely replaced.
1696
1697This code was adapted from the Smalltalk code of Mario Wolczko
1698<mario@wolczko.com>, which is available at
1699ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st
1700
1701C<sdiff> and C<traverse_balanced> were written by Mike Schilli
1702<m@perlmeister.com>.
1703
1704The algorithm is that described in
1705I<A Fast Algorithm for Computing Longest Common Subsequences>,
1706CACM, vol.20, no.5, pp.350-353, May 1977, with a few
1707minor improvements to improve the speed.
1708
1709Much work was done by Ned Konz (perl@bike-nomad.com).
1710
1711The OO interface and some other changes are by Tye McQueen.
1712
1713=cut
diff --git a/pintos-progos/tests/Make.tests b/pintos-progos/tests/Make.tests
new file mode 100644
index 0000000..358e697
--- /dev/null
+++ b/pintos-progos/tests/Make.tests
@@ -0,0 +1,76 @@
1# -*- makefile -*-
2
3include $(patsubst %,$(SRCDIR)/%/Make.tests,$(TEST_SUBDIRS))
4
5PROGS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_PROGS))
6TESTS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_TESTS))
7EXTRA_GRADES = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_EXTRA_GRADES))
8
9OUTPUTS = $(addsuffix .output,$(TESTS) $(EXTRA_GRADES))
10ERRORS = $(addsuffix .errors,$(TESTS) $(EXTRA_GRADES))
11RESULTS = $(addsuffix .result,$(TESTS) $(EXTRA_GRADES))
12
13ifdef PROGS
14include ../../Makefile.userprog
15endif
16
17TIMEOUT = 60
18
19clean::
20 rm -f $(OUTPUTS) $(ERRORS) $(RESULTS)
21
22grade:: results
23 $(SRCDIR)/tests/make-grade $(SRCDIR) $< $(GRADING_FILE) | tee $@
24
25check:: results
26 @cat $<
27 @COUNT="`egrep '^(pass|FAIL) ' $< | wc -l | sed 's/[ ]//g;'`"; \
28 FAILURES="`egrep '^FAIL ' $< | wc -l | sed 's/[ ]//g;'`"; \
29 if [ $$FAILURES = 0 ]; then \
30 echo "All $$COUNT tests passed."; \
31 else \
32 echo "$$FAILURES of $$COUNT tests failed."; \
33 exit 1; \
34 fi
35
36results: $(RESULTS)
37 @for d in $(TESTS) $(EXTRA_GRADES); do \
38 if echo PASS | cmp -s $$d.result -; then \
39 echo "pass $$d"; \
40 else \
41 echo "FAIL $$d"; \
42 fi; \
43 done > $@
44
45outputs:: $(OUTPUTS)
46
47$(foreach prog,$(PROGS),$(eval $(prog).output: $(prog)))
48$(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES)))
49$(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test)))
50
51# Prevent an environment variable VERBOSE from surprising us.
52VERBOSE =
53
54TESTCMD = pintos -v -k -T $(TIMEOUT)
55TESTCMD += $(SIMULATOR)
56TESTCMD += $(PINTOSOPTS)
57ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
58TESTCMD += $(FILESYSSOURCE)
59TESTCMD += $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file)))
60endif
61ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
62TESTCMD += --swap-size=4
63endif
64TESTCMD += -- -q
65TESTCMD += $(KERNELFLAGS)
66ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
67TESTCMD += -f
68endif
69TESTCMD += $(if $($(TEST)_ARGS),run '$(*F) $($(TEST)_ARGS)',run $(*F))
70TESTCMD += < /dev/null
71TESTCMD += 2> $(TEST).errors $(if $(VERBOSE),|tee,>) $(TEST).output
72%.output: kernel.bin loader.bin
73 $(TESTCMD)
74
75%.result: %.ck %.output
76 perl -I$(SRCDIR) $< $* $@
diff --git a/pintos-progos/tests/arc4.c b/pintos-progos/tests/arc4.c
new file mode 100644
index 0000000..b033cc6
--- /dev/null
+++ b/pintos-progos/tests/arc4.c
@@ -0,0 +1,53 @@
1#include <stdint.h>
2#include "tests/arc4.h"
3
4/* Swap bytes. */
5static inline void
6swap_byte (uint8_t *a, uint8_t *b)
7{
8 uint8_t t = *a;
9 *a = *b;
10 *b = t;
11}
12
13void
14arc4_init (struct arc4 *arc4, const void *key_, size_t size)
15{
16 const uint8_t *key = key_;
17 size_t key_idx;
18 uint8_t *s;
19 int i, j;
20
21 s = arc4->s;
22 arc4->i = arc4->j = 0;
23 for (i = 0; i < 256; i++)
24 s[i] = i;
25 for (key_idx = 0, i = j = 0; i < 256; i++)
26 {
27 j = (j + s[i] + key[key_idx]) & 255;
28 swap_byte (s + i, s + j);
29 if (++key_idx >= size)
30 key_idx = 0;
31 }
32}
33
34void
35arc4_crypt (struct arc4 *arc4, void *buf_, size_t size)
36{
37 uint8_t *buf = buf_;
38 uint8_t *s;
39 uint8_t i, j;
40
41 s = arc4->s;
42 i = arc4->i;
43 j = arc4->j;
44 while (size-- > 0)
45 {
46 i += 1;
47 j += s[i];
48 swap_byte (s + i, s + j);
49 *buf++ ^= s[(s[i] + s[j]) & 255];
50 }
51 arc4->i = i;
52 arc4->j = j;
53}
diff --git a/pintos-progos/tests/arc4.h b/pintos-progos/tests/arc4.h
new file mode 100644
index 0000000..61c533a
--- /dev/null
+++ b/pintos-progos/tests/arc4.h
@@ -0,0 +1,17 @@
1#ifndef TESTS_ARC4_H
2#define TESTS_ARC4_H
3
4#include <stddef.h>
5#include <stdint.h>
6
7/* Alleged RC4 algorithm encryption state. */
8struct arc4
9 {
10 uint8_t s[256];
11 uint8_t i, j;
12 };
13
14void arc4_init (struct arc4 *, const void *, size_t);
15void arc4_crypt (struct arc4 *, void *, size_t);
16
17#endif /* tests/arc4.h */
diff --git a/pintos-progos/tests/arc4.pm b/pintos-progos/tests/arc4.pm
new file mode 100644
index 0000000..df19216
--- /dev/null
+++ b/pintos-progos/tests/arc4.pm
@@ -0,0 +1,29 @@
1use strict;
2use warnings;
3
4sub arc4_init {
5 my ($key) = @_;
6 my (@s) = 0...255;
7 my ($j) = 0;
8 for my $i (0...255) {
9 $j = ($j + $s[$i] + ord (substr ($key, $i % length ($key), 1))) & 0xff;
10 @s[$i, $j] = @s[$j, $i];
11 }
12 return (0, 0, @s);
13}
14
15sub arc4_crypt {
16 my ($arc4, $buf) = @_;
17 my ($i, $j, @s) = @$arc4;
18 my ($out) = "";
19 for my $c (split (//, $buf)) {
20 $i = ($i + 1) & 0xff;
21 $j = ($j + $s[$i]) & 0xff;
22 @s[$i, $j] = @s[$j, $i];
23 $out .= chr (ord ($c) ^ $s[($s[$i] + $s[$j]) & 0xff]);
24 }
25 @$arc4 = ($i, $j, @s);
26 return $out;
27}
28
291;
diff --git a/pintos-progos/tests/cksum.c b/pintos-progos/tests/cksum.c
new file mode 100644
index 0000000..92a2995
--- /dev/null
+++ b/pintos-progos/tests/cksum.c
@@ -0,0 +1,92 @@
1/* crctab[] and cksum() are from the `cksum' entry in SUSv3. */
2
3#include <stdint.h>
4#include "tests/cksum.h"
5
6static unsigned long crctab[] = {
7 0x00000000,
8 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
9 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
10 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
11 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
12 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
13 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
14 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
15 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
16 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
17 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
18 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
19 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
20 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
21 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
22 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
23 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
24 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
25 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
26 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
27 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
28 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
29 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
30 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
31 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
32 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
33 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
34 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
35 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
36 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
37 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
38 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
39 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
40 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
41 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
42 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
43 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
44 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
45 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
46 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
47 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
48 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
49 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
50 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
51 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
52 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
53 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
54 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
55 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
56 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
57 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
58 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
59};
60
61/* This is the algorithm used by the Posix `cksum' utility. */
62unsigned long
63cksum (const void *b_, size_t n)
64{
65 const unsigned char *b = b_;
66 uint32_t s = 0;
67 size_t i;
68 for (i = n; i > 0; --i)
69 {
70 unsigned char c = *b++;
71 s = (s << 8) ^ crctab[(s >> 24) ^ c];
72 }
73 while (n != 0)
74 {
75 unsigned char c = n;
76 n >>= 8;
77 s = (s << 8) ^ crctab[(s >> 24) ^ c];
78 }
79 return ~s;
80}
81
82#ifdef STANDALONE_TEST
83#include <stdio.h>
84int
85main (void)
86{
87 char buf[65536];
88 int n = fread (buf, 1, sizeof buf, stdin);
89 printf ("%lu\n", cksum (buf, n));
90 return 0;
91}
92#endif
diff --git a/pintos-progos/tests/cksum.h b/pintos-progos/tests/cksum.h
new file mode 100644
index 0000000..23a1fe9
--- /dev/null
+++ b/pintos-progos/tests/cksum.h
@@ -0,0 +1,8 @@
1#ifndef TESTS_CKSUM_H
2#define TESTS_CKSUM_H
3
4#include <stddef.h>
5
6unsigned long cksum(const void *, size_t);
7
8#endif /* tests/cksum.h */
diff --git a/pintos-progos/tests/cksum.pm b/pintos-progos/tests/cksum.pm
new file mode 100644
index 0000000..73be5f2
--- /dev/null
+++ b/pintos-progos/tests/cksum.pm
@@ -0,0 +1,87 @@
1# From the `cksum' entry in SUSv3.
2
3use strict;
4use warnings;
5
6my (@crctab) =
7 (0x00000000,
8 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
9 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
10 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
11 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
12 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
13 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
14 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
15 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
16 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
17 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
18 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
19 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
20 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
21 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
22 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
23 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
24 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
25 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
26 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
27 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
28 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
29 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
30 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
31 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
32 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
33 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
34 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
35 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
36 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
37 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
38 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
39 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
40 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
41 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
42 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
43 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
44 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
45 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
46 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
47 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
48 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
49 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
50 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
51 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
52 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
53 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
54 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
55 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
56 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
57 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
58 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4);
59
60sub cksum {
61 my ($b) = @_;
62 my ($n) = length ($b);
63 my ($s) = 0;
64 for my $i (0...$n - 1) {
65 my ($c) = ord (substr ($b, $i, 1));
66 $s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
67 $s &= 0xffff_ffff;
68 }
69 while ($n != 0) {
70 my ($c) = $n & 0xff;
71 $n >>= 8;
72 $s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
73 $s &= 0xffff_ffff;
74 }
75 return ~$s & 0xffff_ffff;
76}
77
78sub cksum_file {
79 my ($file) = @_;
80 open (FILE, '<', $file) or die "$file: open: $!\n";
81 my ($data);
82 sysread (FILE, $data, -s FILE) == -s FILE or die "$file: read: $!\n";
83 close (FILE);
84 return cksum ($data);
85}
86
871;
diff --git a/pintos-progos/tests/filesys/Grading.no-vm b/pintos-progos/tests/filesys/Grading.no-vm
new file mode 100644
index 0000000..ee98fc1
--- /dev/null
+++ b/pintos-progos/tests/filesys/Grading.no-vm
@@ -0,0 +1,18 @@
1# Percentage of the testing point total designated for each set of
2# tests.
3
4# This project is primarily about implementing the file system, but
5# all the previous functionality should work too. It's not too easy
6# to screw it up, thus the emphasis.
7
8# 65% for extended file system features.
930% tests/filesys/extended/Rubric.functionality
1015% tests/filesys/extended/Rubric.robustness
1120% tests/filesys/extended/Rubric.persistence
12
13# 20% to not break the provided file system features.
1420% tests/filesys/base/Rubric
15
16# 15% for the rest.
1710% tests/userprog/Rubric.functionality
185% tests/userprog/Rubric.robustness
diff --git a/pintos-progos/tests/filesys/Grading.with-vm b/pintos-progos/tests/filesys/Grading.with-vm
new file mode 100644
index 0000000..e7c041e
--- /dev/null
+++ b/pintos-progos/tests/filesys/Grading.with-vm
@@ -0,0 +1,22 @@
1# Percentage of the testing point total designated for each set of
2# tests.
3
4# This project is primarily about implementing the file system, but
5# all the previous functionality should work too. It's not too easy
6# to screw it up, thus the emphasis.
7
8# 65% for extended file system features.
930% tests/filesys/extended/Rubric.functionality
1015% tests/filesys/extended/Rubric.robustness
1120% tests/filesys/extended/Rubric.persistence
12
13# 20% to not break the provided file system features.
1420% tests/filesys/base/Rubric
15
16# 15% for the rest.
1710% tests/userprog/Rubric.functionality
185% tests/userprog/Rubric.robustness
19
20# Up to 10% bonus for working VM functionality.
218% tests/vm/Rubric.functionality
222% tests/vm/Rubric.robustness
diff --git a/pintos-progos/tests/filesys/base/Make.tests b/pintos-progos/tests/filesys/base/Make.tests
new file mode 100644
index 0000000..e475222
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/Make.tests
@@ -0,0 +1,18 @@
1# -*- makefile -*-
2
3tests/filesys/base_TESTS = $(addprefix tests/filesys/base/,lg-create \
4lg-full lg-random lg-seq-block lg-seq-random sm-create sm-full \
5sm-random sm-seq-block sm-seq-random syn-read syn-remove syn-write)
6
7tests/filesys/base_PROGS = $(tests/filesys/base_TESTS) $(addprefix \
8tests/filesys/base/,child-syn-read child-syn-wrt)
9
10$(foreach prog,$(tests/filesys/base_PROGS), \
11 $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
12$(foreach prog,$(tests/filesys/base_TESTS), \
13 $(eval $(prog)_SRC += tests/main.c))
14
15tests/filesys/base/syn-read_PUTFILES = tests/filesys/base/child-syn-read
16tests/filesys/base/syn-write_PUTFILES = tests/filesys/base/child-syn-wrt
17
18tests/filesys/base/syn-read.output: TIMEOUT = 300
diff --git a/pintos-progos/tests/filesys/base/Rubric b/pintos-progos/tests/filesys/base/Rubric
new file mode 100644
index 0000000..49a9d15
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/Rubric
@@ -0,0 +1,19 @@
1Functionality of base file system:
2- Test basic support for small files.
31 sm-create
42 sm-full
52 sm-random
62 sm-seq-block
73 sm-seq-random
8
9- Test basic support for large files.
101 lg-create
112 lg-full
122 lg-random
132 lg-seq-block
143 lg-seq-random
15
16- Test synchronized multiprogram access to files.
174 syn-read
184 syn-write
192 syn-remove
diff --git a/pintos-progos/tests/filesys/base/child-syn-read.c b/pintos-progos/tests/filesys/base/child-syn-read.c
new file mode 100644
index 0000000..77a5e26
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/child-syn-read.c
@@ -0,0 +1,44 @@
1/* Child process for syn-read test.
2 Reads the contents of a test file a byte at a time, in the
3 hope that this will take long enough that we can get a
4 significant amount of contention in the kernel file system
5 code. */
6
7#include <random.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <syscall.h>
11#include "tests/lib.h"
12#include "tests/filesys/base/syn-read.h"
13
14const char *test_name = "child-syn-read";
15
16static char buf[BUF_SIZE];
17
18int
19main (int argc, const char *argv[])
20{
21 int child_idx;
22 int fd;
23 size_t i;
24
25 quiet = true;
26
27 CHECK (argc == 2, "argc must be 2, actually %d", argc);
28 child_idx = atoi (argv[1]);
29
30 random_init (0);
31 random_bytes (buf, sizeof buf);
32
33 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
34 for (i = 0; i < sizeof buf; i++)
35 {
36 char c;
37 CHECK (read (fd, &c, 1) > 0, "read \"%s\"", file_name);
38 compare_bytes (&c, buf + i, 1, i, file_name);
39 }
40 close (fd);
41
42 return child_idx;
43}
44
diff --git a/pintos-progos/tests/filesys/base/child-syn-wrt.c b/pintos-progos/tests/filesys/base/child-syn-wrt.c
new file mode 100644
index 0000000..1b52584
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/child-syn-wrt.c
@@ -0,0 +1,35 @@
1/* Child process for syn-read test.
2 Writes into part of a test file. Other processes will be
3 writing into other parts at the same time. */
4
5#include <random.h>
6#include <stdlib.h>
7#include <syscall.h>
8#include "tests/lib.h"
9#include "tests/filesys/base/syn-write.h"
10
11char buf[BUF_SIZE];
12
13int
14main (int argc, char *argv[])
15{
16 int child_idx;
17 int fd;
18
19 quiet = true;
20
21 CHECK (argc == 2, "argc must be 2, actually %d", argc);
22 child_idx = atoi (argv[1]);
23
24 random_init (0);
25 random_bytes (buf, sizeof buf);
26
27 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
28 seek (fd, CHUNK_SIZE * child_idx);
29 CHECK (write (fd, buf + CHUNK_SIZE * child_idx, CHUNK_SIZE) > 0,
30 "write \"%s\"", file_name);
31 msg ("close \"%s\"", file_name);
32 close (fd);
33
34 return child_idx;
35}
diff --git a/pintos-progos/tests/filesys/base/full.inc b/pintos-progos/tests/filesys/base/full.inc
new file mode 100644
index 0000000..38a0396
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/full.inc
@@ -0,0 +1,20 @@
1/* -*- c -*- */
2
3#include "tests/filesys/seq-test.h"
4#include "tests/main.h"
5
6static char buf[TEST_SIZE];
7
8static size_t
9return_test_size (void)
10{
11 return TEST_SIZE;
12}
13
14void
15test_main (void)
16{
17 seq_test ("quux",
18 buf, sizeof buf, sizeof buf,
19 return_test_size, NULL);
20}
diff --git a/pintos-progos/tests/filesys/base/lg-create.c b/pintos-progos/tests/filesys/base/lg-create.c
new file mode 100644
index 0000000..5c45eee
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-create.c
@@ -0,0 +1,5 @@
1/* Tests that create properly zeros out the contents of a fairly
2 large file. */
3
4#define TEST_SIZE 75678
5#include "tests/filesys/create.inc"
diff --git a/pintos-progos/tests/filesys/base/lg-create.ck b/pintos-progos/tests/filesys/base/lg-create.ck
new file mode 100644
index 0000000..86b2c51
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-create.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(lg-create) begin
7(lg-create) create "blargle"
8(lg-create) open "blargle" for verification
9(lg-create) verified contents of "blargle"
10(lg-create) close "blargle"
11(lg-create) end
12EOF
13pass;
diff --git a/pintos-progos/tests/filesys/base/lg-full.c b/pintos-progos/tests/filesys/base/lg-full.c
new file mode 100644
index 0000000..5f7234d
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-full.c
@@ -0,0 +1,6 @@
1/* Writes out the contents of a fairly large file all at once,
2 and then reads it back to make sure that it was written
3 properly. */
4
5#define TEST_SIZE 75678
6#include "tests/filesys/base/full.inc"
diff --git a/pintos-progos/tests/filesys/base/lg-full.ck b/pintos-progos/tests/filesys/base/lg-full.ck
new file mode 100644
index 0000000..ee6c7f9
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-full.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(lg-full) begin
7(lg-full) create "quux"
8(lg-full) open "quux"
9(lg-full) writing "quux"
10(lg-full) close "quux"
11(lg-full) open "quux" for verification
12(lg-full) verified contents of "quux"
13(lg-full) close "quux"
14(lg-full) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/lg-random.c b/pintos-progos/tests/filesys/base/lg-random.c
new file mode 100644
index 0000000..b6f8873
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-random.c
@@ -0,0 +1,7 @@
1/* Writes out the content of a fairly large file in random order,
2 then reads it back in random order to verify that it was
3 written properly. */
4
5#define BLOCK_SIZE 512
6#define TEST_SIZE (512 * 150)
7#include "tests/filesys/base/random.inc"
diff --git a/pintos-progos/tests/filesys/base/lg-random.ck b/pintos-progos/tests/filesys/base/lg-random.ck
new file mode 100644
index 0000000..dd9f1dd
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-random.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(lg-random) begin
7(lg-random) create "bazzle"
8(lg-random) open "bazzle"
9(lg-random) write "bazzle" in random order
10(lg-random) read "bazzle" in random order
11(lg-random) close "bazzle"
12(lg-random) end
13EOF
14pass;
diff --git a/pintos-progos/tests/filesys/base/lg-seq-block.c b/pintos-progos/tests/filesys/base/lg-seq-block.c
new file mode 100644
index 0000000..580c30b
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-seq-block.c
@@ -0,0 +1,7 @@
1/* Writes out a fairly large file sequentially, one fixed-size
2 block at a time, then reads it back to verify that it was
3 written properly. */
4
5#define TEST_SIZE 75678
6#define BLOCK_SIZE 513
7#include "tests/filesys/base/seq-block.inc"
diff --git a/pintos-progos/tests/filesys/base/lg-seq-block.ck b/pintos-progos/tests/filesys/base/lg-seq-block.ck
new file mode 100644
index 0000000..b789081
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-seq-block.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(lg-seq-block) begin
7(lg-seq-block) create "noodle"
8(lg-seq-block) open "noodle"
9(lg-seq-block) writing "noodle"
10(lg-seq-block) close "noodle"
11(lg-seq-block) open "noodle" for verification
12(lg-seq-block) verified contents of "noodle"
13(lg-seq-block) close "noodle"
14(lg-seq-block) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/lg-seq-random.c b/pintos-progos/tests/filesys/base/lg-seq-random.c
new file mode 100644
index 0000000..fbb6bba
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-seq-random.c
@@ -0,0 +1,6 @@
1/* Writes out a fairly large file sequentially, one random-sized
2 block at a time, then reads it back to verify that it was
3 written properly. */
4
5#define TEST_SIZE 75678
6#include "tests/filesys/base/seq-random.inc"
diff --git a/pintos-progos/tests/filesys/base/lg-seq-random.ck b/pintos-progos/tests/filesys/base/lg-seq-random.ck
new file mode 100644
index 0000000..6b2dc82
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/lg-seq-random.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(lg-seq-random) begin
7(lg-seq-random) create "nibble"
8(lg-seq-random) open "nibble"
9(lg-seq-random) writing "nibble"
10(lg-seq-random) close "nibble"
11(lg-seq-random) open "nibble" for verification
12(lg-seq-random) verified contents of "nibble"
13(lg-seq-random) close "nibble"
14(lg-seq-random) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/random.inc b/pintos-progos/tests/filesys/base/random.inc
new file mode 100644
index 0000000..eeeea68
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/random.inc
@@ -0,0 +1,59 @@
1/* -*- c -*- */
2
3#include <random.h>
4#include <stdio.h>
5#include <string.h>
6#include <syscall.h>
7#include "tests/lib.h"
8#include "tests/main.h"
9
10#if TEST_SIZE % BLOCK_SIZE != 0
11#error TEST_SIZE must be a multiple of BLOCK_SIZE
12#endif
13
14#define BLOCK_CNT (TEST_SIZE / BLOCK_SIZE)
15
16char buf[TEST_SIZE];
17int order[BLOCK_CNT];
18
19void
20test_main (void)
21{
22 const char *file_name = "bazzle";
23 int fd;
24 size_t i;
25
26 random_init (57);
27 random_bytes (buf, sizeof buf);
28
29 for (i = 0; i < BLOCK_CNT; i++)
30 order[i] = i;
31
32 CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
33 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
34
35 msg ("write \"%s\" in random order", file_name);
36 shuffle (order, BLOCK_CNT, sizeof *order);
37 for (i = 0; i < BLOCK_CNT; i++)
38 {
39 size_t ofs = BLOCK_SIZE * order[i];
40 seek (fd, ofs);
41 if (write (fd, buf + ofs, BLOCK_SIZE) != BLOCK_SIZE)
42 fail ("write %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
43 }
44
45 msg ("read \"%s\" in random order", file_name);
46 shuffle (order, BLOCK_CNT, sizeof *order);
47 for (i = 0; i < BLOCK_CNT; i++)
48 {
49 char block[BLOCK_SIZE];
50 size_t ofs = BLOCK_SIZE * order[i];
51 seek (fd, ofs);
52 if (read (fd, block, BLOCK_SIZE) != BLOCK_SIZE)
53 fail ("read %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
54 compare_bytes (block, buf + ofs, BLOCK_SIZE, ofs, file_name);
55 }
56
57 msg ("close \"%s\"", file_name);
58 close (fd);
59}
diff --git a/pintos-progos/tests/filesys/base/seq-block.inc b/pintos-progos/tests/filesys/base/seq-block.inc
new file mode 100644
index 0000000..d4c1f57
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/seq-block.inc
@@ -0,0 +1,20 @@
1/* -*- c -*- */
2
3#include "tests/filesys/seq-test.h"
4#include "tests/main.h"
5
6static char buf[TEST_SIZE];
7
8static size_t
9return_block_size (void)
10{
11 return BLOCK_SIZE;
12}
13
14void
15test_main (void)
16{
17 seq_test ("noodle",
18 buf, sizeof buf, sizeof buf,
19 return_block_size, NULL);
20}
diff --git a/pintos-progos/tests/filesys/base/seq-random.inc b/pintos-progos/tests/filesys/base/seq-random.inc
new file mode 100644
index 0000000..a4da4c5
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/seq-random.inc
@@ -0,0 +1,22 @@
1/* -*- c -*- */
2
3#include <random.h>
4#include "tests/filesys/seq-test.h"
5#include "tests/main.h"
6
7static char buf[TEST_SIZE];
8
9static size_t
10return_random (void)
11{
12 return random_ulong () % 1031 + 1;
13}
14
15void
16test_main (void)
17{
18 random_init (-1);
19 seq_test ("nibble",
20 buf, sizeof buf, sizeof buf,
21 return_random, NULL);
22}
diff --git a/pintos-progos/tests/filesys/base/sm-create.c b/pintos-progos/tests/filesys/base/sm-create.c
new file mode 100644
index 0000000..6b97ac1
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-create.c
@@ -0,0 +1,5 @@
1/* Tests that create properly zeros out the contents of a fairly
2 small file. */
3
4#define TEST_SIZE 5678
5#include "tests/filesys/create.inc"
diff --git a/pintos-progos/tests/filesys/base/sm-create.ck b/pintos-progos/tests/filesys/base/sm-create.ck
new file mode 100644
index 0000000..8ca80dc
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-create.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(sm-create) begin
7(sm-create) create "blargle"
8(sm-create) open "blargle" for verification
9(sm-create) verified contents of "blargle"
10(sm-create) close "blargle"
11(sm-create) end
12EOF
13pass;
diff --git a/pintos-progos/tests/filesys/base/sm-full.c b/pintos-progos/tests/filesys/base/sm-full.c
new file mode 100644
index 0000000..23ff3d4
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-full.c
@@ -0,0 +1,6 @@
1/* Writes out the contents of a fairly small file all at once,
2 and then reads it back to make sure that it was written
3 properly. */
4
5#define TEST_SIZE 5678
6#include "tests/filesys/base/full.inc"
diff --git a/pintos-progos/tests/filesys/base/sm-full.ck b/pintos-progos/tests/filesys/base/sm-full.ck
new file mode 100644
index 0000000..2e0eb36
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-full.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(sm-full) begin
7(sm-full) create "quux"
8(sm-full) open "quux"
9(sm-full) writing "quux"
10(sm-full) close "quux"
11(sm-full) open "quux" for verification
12(sm-full) verified contents of "quux"
13(sm-full) close "quux"
14(sm-full) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/sm-random.c b/pintos-progos/tests/filesys/base/sm-random.c
new file mode 100644
index 0000000..42d670f
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-random.c
@@ -0,0 +1,7 @@
1/* Writes out the content of a fairly small file in random order,
2 then reads it back in random order to verify that it was
3 written properly. */
4
5#define BLOCK_SIZE 13
6#define TEST_SIZE (13 * 123)
7#include "tests/filesys/base/random.inc"
diff --git a/pintos-progos/tests/filesys/base/sm-random.ck b/pintos-progos/tests/filesys/base/sm-random.ck
new file mode 100644
index 0000000..bda049d
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-random.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(sm-random) begin
7(sm-random) create "bazzle"
8(sm-random) open "bazzle"
9(sm-random) write "bazzle" in random order
10(sm-random) read "bazzle" in random order
11(sm-random) close "bazzle"
12(sm-random) end
13EOF
14pass;
diff --git a/pintos-progos/tests/filesys/base/sm-seq-block.c b/pintos-progos/tests/filesys/base/sm-seq-block.c
new file mode 100644
index 0000000..e368327
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-seq-block.c
@@ -0,0 +1,7 @@
1/* Writes out a fairly small file sequentially, one fixed-size
2 block at a time, then reads it back to verify that it was
3 written properly. */
4
5#define TEST_SIZE 5678
6#define BLOCK_SIZE 513
7#include "tests/filesys/base/seq-block.inc"
diff --git a/pintos-progos/tests/filesys/base/sm-seq-block.ck b/pintos-progos/tests/filesys/base/sm-seq-block.ck
new file mode 100644
index 0000000..0e2939d
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-seq-block.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(sm-seq-block) begin
7(sm-seq-block) create "noodle"
8(sm-seq-block) open "noodle"
9(sm-seq-block) writing "noodle"
10(sm-seq-block) close "noodle"
11(sm-seq-block) open "noodle" for verification
12(sm-seq-block) verified contents of "noodle"
13(sm-seq-block) close "noodle"
14(sm-seq-block) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/sm-seq-random.c b/pintos-progos/tests/filesys/base/sm-seq-random.c
new file mode 100644
index 0000000..89e5b71
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-seq-random.c
@@ -0,0 +1,6 @@
1/* Writes out a fairly large file sequentially, one random-sized
2 block at a time, then reads it back to verify that it was
3 written properly. */
4
5#define TEST_SIZE 5678
6#include "tests/filesys/base/seq-random.inc"
diff --git a/pintos-progos/tests/filesys/base/sm-seq-random.ck b/pintos-progos/tests/filesys/base/sm-seq-random.ck
new file mode 100644
index 0000000..2fb368b
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/sm-seq-random.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(sm-seq-random) begin
7(sm-seq-random) create "nibble"
8(sm-seq-random) open "nibble"
9(sm-seq-random) writing "nibble"
10(sm-seq-random) close "nibble"
11(sm-seq-random) open "nibble" for verification
12(sm-seq-random) verified contents of "nibble"
13(sm-seq-random) close "nibble"
14(sm-seq-random) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/syn-read.c b/pintos-progos/tests/filesys/base/syn-read.c
new file mode 100644
index 0000000..7c36a42
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-read.c
@@ -0,0 +1,31 @@
1/* Spawns 10 child processes, all of which read from the same
2 file and make sure that the contents are what they should
3 be. */
4
5#include <random.h>
6#include <stdio.h>
7#include <syscall.h>
8#include "tests/lib.h"
9#include "tests/main.h"
10#include "tests/filesys/base/syn-read.h"
11
12static char buf[BUF_SIZE];
13
14#define CHILD_CNT 10
15
16void
17test_main (void)
18{
19 pid_t children[CHILD_CNT];
20 int fd;
21
22 CHECK (create (file_name, sizeof buf), "create \"%s\"", file_name);
23 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
24 random_bytes (buf, sizeof buf);
25 CHECK (write (fd, buf, sizeof buf) > 0, "write \"%s\"", file_name);
26 msg ("close \"%s\"", file_name);
27 close (fd);
28
29 exec_children ("child-syn-read", children, CHILD_CNT);
30 wait_children (children, CHILD_CNT);
31}
diff --git a/pintos-progos/tests/filesys/base/syn-read.ck b/pintos-progos/tests/filesys/base/syn-read.ck
new file mode 100644
index 0000000..e2f68e8
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-read.ck
@@ -0,0 +1,33 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(syn-read) begin
7(syn-read) create "data"
8(syn-read) open "data"
9(syn-read) write "data"
10(syn-read) close "data"
11(syn-read) exec child 1 of 10: "child-syn-read 0"
12(syn-read) exec child 2 of 10: "child-syn-read 1"
13(syn-read) exec child 3 of 10: "child-syn-read 2"
14(syn-read) exec child 4 of 10: "child-syn-read 3"
15(syn-read) exec child 5 of 10: "child-syn-read 4"
16(syn-read) exec child 6 of 10: "child-syn-read 5"
17(syn-read) exec child 7 of 10: "child-syn-read 6"
18(syn-read) exec child 8 of 10: "child-syn-read 7"
19(syn-read) exec child 9 of 10: "child-syn-read 8"
20(syn-read) exec child 10 of 10: "child-syn-read 9"
21(syn-read) wait for child 1 of 10 returned 0 (expected 0)
22(syn-read) wait for child 2 of 10 returned 1 (expected 1)
23(syn-read) wait for child 3 of 10 returned 2 (expected 2)
24(syn-read) wait for child 4 of 10 returned 3 (expected 3)
25(syn-read) wait for child 5 of 10 returned 4 (expected 4)
26(syn-read) wait for child 6 of 10 returned 5 (expected 5)
27(syn-read) wait for child 7 of 10 returned 6 (expected 6)
28(syn-read) wait for child 8 of 10 returned 7 (expected 7)
29(syn-read) wait for child 9 of 10 returned 8 (expected 8)
30(syn-read) wait for child 10 of 10 returned 9 (expected 9)
31(syn-read) end
32EOF
33pass;
diff --git a/pintos-progos/tests/filesys/base/syn-read.h b/pintos-progos/tests/filesys/base/syn-read.h
new file mode 100644
index 0000000..bff8082
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-read.h
@@ -0,0 +1,7 @@
1#ifndef TESTS_FILESYS_BASE_SYN_READ_H
2#define TESTS_FILESYS_BASE_SYN_READ_H
3
4#define BUF_SIZE 1024
5static const char file_name[] = "data";
6
7#endif /* tests/filesys/base/syn-read.h */
diff --git a/pintos-progos/tests/filesys/base/syn-remove.c b/pintos-progos/tests/filesys/base/syn-remove.c
new file mode 100644
index 0000000..c9ba110
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-remove.c
@@ -0,0 +1,30 @@
1/* Verifies that a deleted file may still be written to and read
2 from. */
3
4#include <random.h>
5#include <string.h>
6#include <syscall.h>
7#include "tests/lib.h"
8#include "tests/main.h"
9
10char buf1[1234];
11char buf2[1234];
12
13void
14test_main (void)
15{
16 const char *file_name = "deleteme";
17 int fd;
18
19 CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
20 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
21 CHECK (remove (file_name), "remove \"%s\"", file_name);
22 random_bytes (buf1, sizeof buf1);
23 CHECK (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", file_name);
24 msg ("seek \"%s\" to 0", file_name);
25 seek (fd, 0);
26 CHECK (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", file_name);
27 compare_bytes (buf2, buf1, sizeof buf1, 0, file_name);
28 msg ("close \"%s\"", file_name);
29 close (fd);
30}
diff --git a/pintos-progos/tests/filesys/base/syn-remove.ck b/pintos-progos/tests/filesys/base/syn-remove.ck
new file mode 100644
index 0000000..16ff11e
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-remove.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(syn-remove) begin
7(syn-remove) create "deleteme"
8(syn-remove) open "deleteme"
9(syn-remove) remove "deleteme"
10(syn-remove) write "deleteme"
11(syn-remove) seek "deleteme" to 0
12(syn-remove) read "deleteme"
13(syn-remove) close "deleteme"
14(syn-remove) end
15EOF
16pass;
diff --git a/pintos-progos/tests/filesys/base/syn-write.c b/pintos-progos/tests/filesys/base/syn-write.c
new file mode 100644
index 0000000..1439862
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-write.c
@@ -0,0 +1,31 @@
1/* Spawns several child processes to write out different parts of
2 the contents of a file and waits for them to finish. Then
3 reads back the file and verifies its contents. */
4
5#include <random.h>
6#include <stdio.h>
7#include <string.h>
8#include <syscall.h>
9#include "tests/filesys/base/syn-write.h"
10#include "tests/lib.h"
11#include "tests/main.h"
12
13char buf1[BUF_SIZE];
14char buf2[BUF_SIZE];
15
16void
17test_main (void)
18{
19 pid_t children[CHILD_CNT];
20 int fd;
21
22 CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
23
24 exec_children ("child-syn-wrt", children, CHILD_CNT);
25 wait_children (children, CHILD_CNT);
26
27 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
28 CHECK (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", file_name);
29 random_bytes (buf2, sizeof buf2);
30 compare_bytes (buf1, buf2, sizeof buf1, 0, file_name);
31}
diff --git a/pintos-progos/tests/filesys/base/syn-write.ck b/pintos-progos/tests/filesys/base/syn-write.ck
new file mode 100644
index 0000000..629a7a2
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-write.ck
@@ -0,0 +1,32 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(syn-write) begin
7(syn-write) create "stuff"
8(syn-write) exec child 1 of 10: "child-syn-wrt 0"
9(syn-write) exec child 2 of 10: "child-syn-wrt 1"
10(syn-write) exec child 3 of 10: "child-syn-wrt 2"
11(syn-write) exec child 4 of 10: "child-syn-wrt 3"
12(syn-write) exec child 5 of 10: "child-syn-wrt 4"
13(syn-write) exec child 6 of 10: "child-syn-wrt 5"
14(syn-write) exec child 7 of 10: "child-syn-wrt 6"
15(syn-write) exec child 8 of 10: "child-syn-wrt 7"
16(syn-write) exec child 9 of 10: "child-syn-wrt 8"
17(syn-write) exec child 10 of 10: "child-syn-wrt 9"
18(syn-write) wait for child 1 of 10 returned 0 (expected 0)
19(syn-write) wait for child 2 of 10 returned 1 (expected 1)
20(syn-write) wait for child 3 of 10 returned 2 (expected 2)
21(syn-write) wait for child 4 of 10 returned 3 (expected 3)
22(syn-write) wait for child 5 of 10 returned 4 (expected 4)
23(syn-write) wait for child 6 of 10 returned 5 (expected 5)
24(syn-write) wait for child 7 of 10 returned 6 (expected 6)
25(syn-write) wait for child 8 of 10 returned 7 (expected 7)
26(syn-write) wait for child 9 of 10 returned 8 (expected 8)
27(syn-write) wait for child 10 of 10 returned 9 (expected 9)
28(syn-write) open "stuff"
29(syn-write) read "stuff"
30(syn-write) end
31EOF
32pass;
diff --git a/pintos-progos/tests/filesys/base/syn-write.h b/pintos-progos/tests/filesys/base/syn-write.h
new file mode 100644
index 0000000..07a6d5a
--- /dev/null
+++ b/pintos-progos/tests/filesys/base/syn-write.h
@@ -0,0 +1,9 @@
1#ifndef TESTS_FILESYS_BASE_SYN_WRITE_H
2#define TESTS_FILESYS_BASE_SYN_WRITE_H
3
4#define CHILD_CNT 10
5#define CHUNK_SIZE 512
6#define BUF_SIZE (CHILD_CNT * CHUNK_SIZE)
7static const char file_name[] = "stuff";
8
9#endif /* tests/filesys/base/syn-write.h */
diff --git a/pintos-progos/tests/filesys/create.inc b/pintos-progos/tests/filesys/create.inc
new file mode 100644
index 0000000..4baf771
--- /dev/null
+++ b/pintos-progos/tests/filesys/create.inc
@@ -0,0 +1,15 @@
1/* -*- c -*- */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7static char buf[TEST_SIZE];
8
9void
10test_main (void)
11{
12 const char *file_name = "blargle";
13 CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
14 check_file (file_name, buf, TEST_SIZE);
15}
diff --git a/pintos-progos/tests/filesys/extended/Make.tests b/pintos-progos/tests/filesys/extended/Make.tests
new file mode 100644
index 0000000..e03b98d
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/Make.tests
@@ -0,0 +1,61 @@
1# -*- makefile -*-
2
3raw_tests = dir-empty-name dir-mk-tree dir-mkdir dir-open \
4dir-over-file dir-rm-cwd dir-rm-parent dir-rm-root dir-rm-tree \
5dir-rmdir dir-under-file dir-vine grow-create grow-dir-lg \
6grow-file-size grow-root-lg grow-root-sm grow-seq-lg grow-seq-sm \
7grow-sparse grow-tell grow-two-files syn-rw
8
9tests/filesys/extended_TESTS = $(patsubst %,tests/filesys/extended/%,$(raw_tests))
10tests/filesys/extended_EXTRA_GRADES = $(patsubst %,tests/filesys/extended/%-persistence,$(raw_tests))
11
12tests/filesys/extended_PROGS = $(tests/filesys/extended_TESTS) \
13tests/filesys/extended/child-syn-rw tests/filesys/extended/tar
14
15$(foreach prog,$(tests/filesys/extended_PROGS), \
16 $(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
17$(foreach prog,$(tests/filesys/extended_TESTS), \
18 $(eval $(prog)_SRC += tests/main.c))
19$(foreach prog,$(tests/filesys/extended_TESTS), \
20 $(eval $(prog)_PUTFILES += tests/filesys/extended/tar))
21# The version of GNU make 3.80 on vine barfs if this is split at
22# the last comma.
23$(foreach test,$(tests/filesys/extended_TESTS),$(eval $(test).output: FILESYSSOURCE = --disk=tmp.dsk))
24
25tests/filesys/extended/dir-mk-tree_SRC += tests/filesys/extended/mk-tree.c
26tests/filesys/extended/dir-rm-tree_SRC += tests/filesys/extended/mk-tree.c
27
28tests/filesys/extended/syn-rw_PUTFILES += tests/filesys/extended/child-syn-rw
29
30tests/filesys/extended/dir-vine.output: TIMEOUT = 150
31
32GETTIMEOUT = 60
33
34GETCMD = pintos -v -k -T $(GETTIMEOUT)
35GETCMD += $(PINTOSOPTS)
36GETCMD += $(SIMULATOR)
37GETCMD += $(FILESYSSOURCE)
38GETCMD += -g fs.tar -a $(TEST).tar
39ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
40GETCMD += --swap-size=4
41endif
42GETCMD += -- -q
43GETCMD += $(KERNELFLAGS)
44GETCMD += run 'tar fs.tar /'
45GETCMD += < /dev/null
46GETCMD += 2> $(TEST)-persistence.errors $(if $(VERBOSE),|tee,>) $(TEST)-persistence.output
47
48tests/filesys/extended/%.output: kernel.bin
49 rm -f tmp.dsk
50 pintos-mkdisk tmp.dsk --filesys-size=2
51 $(TESTCMD)
52 $(GETCMD)
53 rm -f tmp.dsk
54$(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.output: tests/filesys/extended/$(raw_test).output))
55$(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.result: tests/filesys/extended/$(raw_test).result))
56
57TARS = $(addsuffix .tar,$(tests/filesys/extended_TESTS))
58
59clean::
60 rm -f $(TARS)
61 rm -f tests/filesys/extended/can-rmdir-cwd
diff --git a/pintos-progos/tests/filesys/extended/Rubric.functionality b/pintos-progos/tests/filesys/extended/Rubric.functionality
new file mode 100644
index 0000000..91ed6f0
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/Rubric.functionality
@@ -0,0 +1,26 @@
1Functionality of extended file system:
2- Test directory support.
31 dir-mkdir
43 dir-mk-tree
5
61 dir-rmdir
73 dir-rm-tree
8
95 dir-vine
10
11- Test file growth.
121 grow-create
131 grow-seq-sm
143 grow-seq-lg
153 grow-sparse
163 grow-two-files
171 grow-tell
181 grow-file-size
19
20- Test directory growth.
211 grow-dir-lg
221 grow-root-sm
231 grow-root-lg
24
25- Test writing from multiple processes.
265 syn-rw
diff --git a/pintos-progos/tests/filesys/extended/Rubric.persistence b/pintos-progos/tests/filesys/extended/Rubric.persistence
new file mode 100644
index 0000000..405620a
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/Rubric.persistence
@@ -0,0 +1,24 @@
1Persistence of file system:
21 dir-empty-name-persistence
31 dir-mk-tree-persistence
41 dir-mkdir-persistence
51 dir-open-persistence
61 dir-over-file-persistence
71 dir-rm-cwd-persistence
81 dir-rm-parent-persistence
91 dir-rm-root-persistence
101 dir-rm-tree-persistence
111 dir-rmdir-persistence
121 dir-under-file-persistence
131 dir-vine-persistence
141 grow-create-persistence
151 grow-dir-lg-persistence
161 grow-file-size-persistence
171 grow-root-lg-persistence
181 grow-root-sm-persistence
191 grow-seq-lg-persistence
201 grow-seq-sm-persistence
211 grow-sparse-persistence
221 grow-tell-persistence
231 grow-two-files-persistence
241 syn-rw-persistence
diff --git a/pintos-progos/tests/filesys/extended/Rubric.robustness b/pintos-progos/tests/filesys/extended/Rubric.robustness
new file mode 100644
index 0000000..fb9f32f
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/Rubric.robustness
@@ -0,0 +1,9 @@
1Robustness of file system:
21 dir-empty-name
31 dir-open
41 dir-over-file
51 dir-under-file
6
73 dir-rm-cwd
82 dir-rm-parent
91 dir-rm-root
diff --git a/pintos-progos/tests/filesys/extended/child-syn-rw.c b/pintos-progos/tests/filesys/extended/child-syn-rw.c
new file mode 100644
index 0000000..0e2217d
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/child-syn-rw.c
@@ -0,0 +1,53 @@
1/* Child process for syn-rw.
2 Reads from a file created by our parent process, which is
3 growing it. We loop until we've read the whole file
4 successfully. Many iterations through the loop will return 0
5 bytes, because the file has not grown in the meantime. That
6 is, we are "busy waiting" for the file to grow.
7 (This test could be improved by adding a "yield" system call
8 and calling yield whenever we receive a 0-byte read.) */
9
10#include <random.h>
11#include <stdlib.h>
12#include <syscall.h>
13#include "tests/filesys/extended/syn-rw.h"
14#include "tests/lib.h"
15
16const char *test_name = "child-syn-rw";
17
18static char buf1[BUF_SIZE];
19static char buf2[BUF_SIZE];
20
21int
22main (int argc, const char *argv[])
23{
24 int child_idx;
25 int fd;
26 size_t ofs;
27
28 quiet = true;
29
30 CHECK (argc == 2, "argc must be 2, actually %d", argc);
31 child_idx = atoi (argv[1]);
32
33 random_init (0);
34 random_bytes (buf1, sizeof buf1);
35
36 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
37 ofs = 0;
38 while (ofs < sizeof buf2)
39 {
40 int bytes_read = read (fd, buf2 + ofs, sizeof buf2 - ofs);
41 CHECK (bytes_read >= -1 && bytes_read <= (int) (sizeof buf2 - ofs),
42 "%zu-byte read on \"%s\" returned invalid value of %d",
43 sizeof buf2 - ofs, file_name, bytes_read);
44 if (bytes_read > 0)
45 {
46 compare_bytes (buf2 + ofs, buf1 + ofs, bytes_read, ofs, file_name);
47 ofs += bytes_read;
48 }
49 }
50 close (fd);
51
52 return child_idx;
53}
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck b/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-empty-name-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name.c b/pintos-progos/tests/filesys/extended/dir-empty-name.c
new file mode 100644
index 0000000..c4859d2
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-empty-name.c
@@ -0,0 +1,12 @@
1/* Tries to create a directory named as the empty string,
2 which must return failure. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (!mkdir (""), "mkdir \"\" (must return false)");
12}
diff --git a/pintos-progos/tests/filesys/extended/dir-empty-name.ck b/pintos-progos/tests/filesys/extended/dir-empty-name.ck
new file mode 100644
index 0000000..d6c5621
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-empty-name.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-empty-name) begin
7(dir-empty-name) mkdir "" (must return false)
8(dir-empty-name) end
9EOF
10pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck b/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck
new file mode 100644
index 0000000..fb16afd
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mk-tree-persistence.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5my ($tree);
6for my $a (0...3) {
7 for my $b (0...2) {
8 for my $c (0...2) {
9 for my $d (0...3) {
10 $tree->{$a}{$b}{$c}{$d} = [''];
11 }
12 }
13 }
14}
15check_archive ($tree);
16pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree.c b/pintos-progos/tests/filesys/extended/dir-mk-tree.c
new file mode 100644
index 0000000..a714ff3
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mk-tree.c
@@ -0,0 +1,12 @@
1/* Creates directories /0/0/0 through /3/2/2 and creates files in
2 the leaf directories. */
3
4#include "tests/filesys/extended/mk-tree.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 make_tree (4, 3, 3, 4);
11}
12
diff --git a/pintos-progos/tests/filesys/extended/dir-mk-tree.ck b/pintos-progos/tests/filesys/extended/dir-mk-tree.ck
new file mode 100644
index 0000000..a8507e2
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mk-tree.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-mk-tree) begin
7(dir-mk-tree) creating /0/0/0/0 through /3/2/2/3...
8(dir-mk-tree) open "/0/2/0/3"
9(dir-mk-tree) close "/0/2/0/3"
10(dir-mk-tree) end
11EOF
12pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck b/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck
new file mode 100644
index 0000000..7682900
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mkdir-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({'a' => {'b' => ["\0" x 512]}});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir.c b/pintos-progos/tests/filesys/extended/dir-mkdir.c
new file mode 100644
index 0000000..994f41c
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mkdir.c
@@ -0,0 +1,15 @@
1/* Tests mkdir(). */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 CHECK (mkdir ("a"), "mkdir \"a\"");
11 CHECK (create ("a/b", 512), "create \"a/b\"");
12 CHECK (chdir ("a"), "chdir \"a\"");
13 CHECK (open ("b") > 1, "open \"b\"");
14}
15
diff --git a/pintos-progos/tests/filesys/extended/dir-mkdir.ck b/pintos-progos/tests/filesys/extended/dir-mkdir.ck
new file mode 100644
index 0000000..4644f80
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-mkdir.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-mkdir) begin
7(dir-mkdir) mkdir "a"
8(dir-mkdir) create "a/b"
9(dir-mkdir) chdir "a"
10(dir-mkdir) open "b"
11(dir-mkdir) end
12EOF
13pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-open-persistence.ck b/pintos-progos/tests/filesys/extended/dir-open-persistence.ck
new file mode 100644
index 0000000..26ff2f1
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-open-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"xyzzy" => {}});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-open.c b/pintos-progos/tests/filesys/extended/dir-open.c
new file mode 100644
index 0000000..29d18b8
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-open.c
@@ -0,0 +1,21 @@
1/* Opens a directory, then tries to write to it, which must
2 fail. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int fd;
12 int retval;
13
14 CHECK (mkdir ("xyzzy"), "mkdir \"xyzzy\"");
15 CHECK ((fd = open ("xyzzy")) > 1, "open \"xyzzy\"");
16
17 msg ("write \"xyzzy\"");
18 retval = write (fd, "foobar", 6);
19 CHECK (retval == -1,
20 "write \"xyzzy\" (must return -1, actually %d)", retval);
21}
diff --git a/pintos-progos/tests/filesys/extended/dir-open.ck b/pintos-progos/tests/filesys/extended/dir-open.ck
new file mode 100644
index 0000000..fccc563
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-open.ck
@@ -0,0 +1,20 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(dir-open) begin
7(dir-open) mkdir "xyzzy"
8(dir-open) open "xyzzy"
9(dir-open) write "xyzzy"
10(dir-open) write "xyzzy" (must return -1, actually -1)
11(dir-open) end
12dir-open: exit(0)
13EOF
14(dir-open) begin
15(dir-open) mkdir "xyzzy"
16(dir-open) open "xyzzy"
17(dir-open) write "xyzzy"
18dir-open: exit(-1)
19EOF
20pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck b/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck
new file mode 100644
index 0000000..56b4ed2
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-over-file-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"abc" => {}});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file.c b/pintos-progos/tests/filesys/extended/dir-over-file.c
new file mode 100644
index 0000000..cdd2c62
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-over-file.c
@@ -0,0 +1,13 @@
1/* Tries to create a file with the same name as an existing
2 directory, which must return failure. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (mkdir ("abc"), "mkdir \"abc\"");
12 CHECK (!create ("abc", 0), "create \"abc\" (must return false)");
13}
diff --git a/pintos-progos/tests/filesys/extended/dir-over-file.ck b/pintos-progos/tests/filesys/extended/dir-over-file.ck
new file mode 100644
index 0000000..aae1c1e
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-over-file.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-over-file) begin
7(dir-over-file) mkdir "abc"
8(dir-over-file) create "abc" (must return false)
9(dir-over-file) end
10EOF
11pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck
new file mode 100644
index 0000000..7533570
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd-persistence.ck
@@ -0,0 +1,8 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5my ($cwd_removable) = read_text_file ("tests/filesys/extended/can-rmdir-cwd");
6$cwd_removable eq 'YES' || $cwd_removable eq 'NO' or die;
7check_archive ($cwd_removable eq 'YES' ? {} : {"a" => {}});
8pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd.c b/pintos-progos/tests/filesys/extended/dir-rm-cwd.c
new file mode 100644
index 0000000..78e13de
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd.c
@@ -0,0 +1,75 @@
1/* Tries to remove the current directory, which may succeed or
2 fail. The requirements in each case are different; refer to
3 the assignment for details. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9static int
10wrap_open (const char *name)
11{
12 static int fds[8], fd_cnt;
13 int fd, i;
14
15 CHECK ((fd = open (name)) > 1, "open \"%s\"", name);
16 for (i = 0; i < fd_cnt; i++)
17 if (fds[i] == fd)
18 fail ("fd returned is not unique");
19 fds[fd_cnt++] = fd;
20 return fd;
21}
22
23void
24test_main (void)
25{
26 int root_fd, a_fd0;
27 char name[READDIR_MAX_LEN + 1];
28
29 root_fd = wrap_open ("/");
30 CHECK (mkdir ("a"), "mkdir \"a\"");
31
32 a_fd0 = wrap_open ("/a");
33 CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty");
34 CHECK (inumber (root_fd) != inumber (a_fd0),
35 "\"/\" and \"/a\" must have different inumbers");
36
37 CHECK (chdir ("a"), "chdir \"a\"");
38
39 msg ("try to remove \"/a\"");
40 if (remove ("/a"))
41 {
42 msg ("remove successful");
43
44 CHECK (open ("/a") == -1, "open \"/a\" (must fail)");
45 CHECK (open (".") == -1, "open \".\" (must fail)");
46 CHECK (open ("..") == -1, "open \"..\" (must fail)");
47 CHECK (!create ("x", 512), "create \"x\" (must fail)");
48 }
49 else
50 {
51 int a_fd1, a_fd2, a_fd3;
52
53 msg ("remove failed");
54
55 CHECK (!remove ("../a"), "try to remove \"../a\" (must fail)");
56 CHECK (!remove (".././a"), "try to remove \".././a\" (must fail)");
57 CHECK (!remove ("/./a"), "try to remove \"/./a\" (must fail)");
58
59 a_fd1 = wrap_open ("/a");
60 a_fd2 = wrap_open (".");
61 CHECK (inumber (a_fd1) == inumber (a_fd2),
62 "\"/a\" and \".\" must have same inumber");
63 CHECK (inumber (root_fd) != inumber (a_fd1),
64 "\"/\" and \"/a\" must have different inumbers");
65
66 CHECK (chdir ("/a"), "chdir \"/a\"");
67 a_fd3 = wrap_open (".");
68 CHECK (inumber (a_fd3) == inumber (a_fd1),
69 "\".\" must have same inumber as before");
70
71 CHECK (chdir ("/"), "chdir \"/\"");
72 CHECK (!remove ("a"), "try to remove \"a\" (must fail: still open)");
73 }
74 CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty");
75}
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck b/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck
new file mode 100644
index 0000000..6fa4739
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-cwd.ck
@@ -0,0 +1,51 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5my ($cwd_removable) = check_expected (IGNORE_EXIT_CODES => 1,
6 {NO => <<'EOF', YES => <<'EOF'});
7(dir-rm-cwd) begin
8(dir-rm-cwd) open "/"
9(dir-rm-cwd) mkdir "a"
10(dir-rm-cwd) open "/a"
11(dir-rm-cwd) verify "/a" is empty
12(dir-rm-cwd) "/" and "/a" must have different inumbers
13(dir-rm-cwd) chdir "a"
14(dir-rm-cwd) try to remove "/a"
15(dir-rm-cwd) remove failed
16(dir-rm-cwd) try to remove "../a" (must fail)
17(dir-rm-cwd) try to remove ".././a" (must fail)
18(dir-rm-cwd) try to remove "/./a" (must fail)
19(dir-rm-cwd) open "/a"
20(dir-rm-cwd) open "."
21(dir-rm-cwd) "/a" and "." must have same inumber
22(dir-rm-cwd) "/" and "/a" must have different inumbers
23(dir-rm-cwd) chdir "/a"
24(dir-rm-cwd) open "."
25(dir-rm-cwd) "." must have same inumber as before
26(dir-rm-cwd) chdir "/"
27(dir-rm-cwd) try to remove "a" (must fail: still open)
28(dir-rm-cwd) verify "/a" is empty
29(dir-rm-cwd) end
30EOF
31(dir-rm-cwd) begin
32(dir-rm-cwd) open "/"
33(dir-rm-cwd) mkdir "a"
34(dir-rm-cwd) open "/a"
35(dir-rm-cwd) verify "/a" is empty
36(dir-rm-cwd) "/" and "/a" must have different inumbers
37(dir-rm-cwd) chdir "a"
38(dir-rm-cwd) try to remove "/a"
39(dir-rm-cwd) remove successful
40(dir-rm-cwd) open "/a" (must fail)
41(dir-rm-cwd) open "." (must fail)
42(dir-rm-cwd) open ".." (must fail)
43(dir-rm-cwd) create "x" (must fail)
44(dir-rm-cwd) verify "/a" is empty
45(dir-rm-cwd) end
46EOF
47open (CAN_RMDIR_CWD, ">tests/filesys/extended/can-rmdir-cwd")
48 or die "tests/filesys/extended/can-rmdir-cwd: create: $!\n";
49print CAN_RMDIR_CWD "$cwd_removable";
50close (CAN_RMDIR_CWD);
51pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck
new file mode 100644
index 0000000..f30b04a
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-parent-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"a" => {"b" => {}}});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent.c b/pintos-progos/tests/filesys/extended/dir-rm-parent.c
new file mode 100644
index 0000000..eb43f5b
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-parent.c
@@ -0,0 +1,16 @@
1/* Tries to remove a parent of the current directory. This must
2 fail, because that directory is non-empty. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (mkdir ("a"), "mkdir \"a\"");
12 CHECK (chdir ("a"), "chdir \"a\"");
13 CHECK (mkdir ("b"), "mkdir \"b\"");
14 CHECK (chdir ("b"), "chdir \"b\"");
15 CHECK (!remove ("/a"), "remove \"/a\" (must fail)");
16}
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-parent.ck b/pintos-progos/tests/filesys/extended/dir-rm-parent.ck
new file mode 100644
index 0000000..9fea8f2
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-parent.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-rm-parent) begin
7(dir-rm-parent) mkdir "a"
8(dir-rm-parent) chdir "a"
9(dir-rm-parent) mkdir "b"
10(dir-rm-parent) chdir "b"
11(dir-rm-parent) remove "/a" (must fail)
12(dir-rm-parent) end
13EOF
14pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck
new file mode 100644
index 0000000..6315107
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-root-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"a" => ["\0" x 243]});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root.c b/pintos-progos/tests/filesys/extended/dir-rm-root.c
new file mode 100644
index 0000000..c47f1eb
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-root.c
@@ -0,0 +1,13 @@
1/* Try to remove the root directory.
2 This must fail. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (!remove ("/"), "remove \"/\" (must fail)");
12 CHECK (create ("/a", 243), "create \"/a\"");
13}
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-root.ck b/pintos-progos/tests/filesys/extended/dir-rm-root.ck
new file mode 100644
index 0000000..8a69ff3
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-root.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-rm-root) begin
7(dir-rm-root) remove "/" (must fail)
8(dir-rm-root) create "/a"
9(dir-rm-root) end
10EOF
11pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-tree-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree.c b/pintos-progos/tests/filesys/extended/dir-rm-tree.c
new file mode 100644
index 0000000..bab41a6
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-tree.c
@@ -0,0 +1,62 @@
1/* Creates directories /0/0/0 through /3/2/2 and files in the
2 leaf directories, then removes them. */
3
4#include <stdarg.h>
5#include <stdio.h>
6#include <syscall.h>
7#include "tests/filesys/extended/mk-tree.h"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11static void remove_tree (int at, int bt, int ct, int dt);
12
13void
14test_main (void)
15{
16 make_tree (4, 3, 3, 4);
17 remove_tree (4, 3, 3, 4);
18}
19
20static void do_remove (const char *format, ...) PRINTF_FORMAT (1, 2);
21
22static void
23remove_tree (int at, int bt, int ct, int dt)
24{
25 char try[128];
26 int a, b, c, d;
27
28 msg ("removing /0/0/0/0 through /%d/%d/%d/%d...",
29 at - 1, bt - 1, ct - 1, dt - 1);
30 quiet = true;
31 for (a = 0; a < at; a++)
32 {
33 for (b = 0; b < bt; b++)
34 {
35 for (c = 0; c < ct; c++)
36 {
37 for (d = 0; d < dt; d++)
38 do_remove ("/%d/%d/%d/%d", a, b, c, d);
39 do_remove ("/%d/%d/%d", a, b, c);
40 }
41 do_remove ("/%d/%d", a, b);
42 }
43 do_remove ("/%d", a);
44 }
45 quiet = false;
46
47 snprintf (try, sizeof (try), "/%d/%d/%d/%d", at - 1, 0, ct - 1, 0);
48 CHECK (open (try) == -1, "open \"%s\" (must return -1)", try);
49}
50
51static void
52do_remove (const char *format, ...)
53{
54 char name[128];
55 va_list args;
56
57 va_start (args, format);
58 vsnprintf (name, sizeof name, format, args);
59 va_end (args);
60
61 CHECK (remove (name), "remove \"%s\"", name);
62}
diff --git a/pintos-progos/tests/filesys/extended/dir-rm-tree.ck b/pintos-progos/tests/filesys/extended/dir-rm-tree.ck
new file mode 100644
index 0000000..587b493
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rm-tree.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-rm-tree) begin
7(dir-rm-tree) creating /0/0/0/0 through /3/2/2/3...
8(dir-rm-tree) open "/0/2/0/3"
9(dir-rm-tree) close "/0/2/0/3"
10(dir-rm-tree) removing /0/0/0/0 through /3/2/2/3...
11(dir-rm-tree) open "/3/0/2/0" (must return -1)
12(dir-rm-tree) end
13EOF
14pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck b/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rmdir-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir.c b/pintos-progos/tests/filesys/extended/dir-rmdir.c
new file mode 100644
index 0000000..b3cbc6f
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rmdir.c
@@ -0,0 +1,14 @@
1/* Creates and removes a directory, then makes sure that it's
2 really gone. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (mkdir ("a"), "mkdir \"a\"");
12 CHECK (remove ("a"), "rmdir \"a\"");
13 CHECK (!chdir ("a"), "chdir \"a\" (must return false)");
14}
diff --git a/pintos-progos/tests/filesys/extended/dir-rmdir.ck b/pintos-progos/tests/filesys/extended/dir-rmdir.ck
new file mode 100644
index 0000000..e0d8922
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-rmdir.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-rmdir) begin
7(dir-rmdir) mkdir "a"
8(dir-rmdir) rmdir "a"
9(dir-rmdir) chdir "a" (must return false)
10(dir-rmdir) end
11EOF
12pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck b/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck
new file mode 100644
index 0000000..67ca528
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-under-file-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"abc" => ['']});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file.c b/pintos-progos/tests/filesys/extended/dir-under-file.c
new file mode 100644
index 0000000..973a8b1
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-under-file.c
@@ -0,0 +1,13 @@
1/* Tries to create a directory with the same name as an existing
2 file, which must return failure. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (create ("abc", 0), "create \"abc\"");
12 CHECK (!mkdir ("abc"), "mkdir \"abc\" (must return false)");
13}
diff --git a/pintos-progos/tests/filesys/extended/dir-under-file.ck b/pintos-progos/tests/filesys/extended/dir-under-file.ck
new file mode 100644
index 0000000..cce23b4
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-under-file.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-under-file) begin
7(dir-under-file) create "abc"
8(dir-under-file) mkdir "abc" (must return false)
9(dir-under-file) end
10EOF
11pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck b/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck
new file mode 100644
index 0000000..698ef01
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-vine-persistence.ck
@@ -0,0 +1,37 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5# The archive should look like this:
6#
7# 40642 dir-vine
8# 42479 tar
9# 0 start
10# 11 start/file0
11# 0 start/dir0
12# 11 start/dir0/file1
13# 0 start/dir0/dir1
14# 11 start/dir0/dir1/file2
15# 0 start/dir0/dir1/dir2
16# 11 start/dir0/dir1/dir2/file3
17# 0 start/dir0/dir1/dir2/dir3
18# 11 start/dir0/dir1/dir2/dir3/file4
19# 0 start/dir0/dir1/dir2/dir3/dir4
20# 11 start/dir0/dir1/dir2/dir3/dir4/file5
21# 0 start/dir0/dir1/dir2/dir3/dir4/dir5
22# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/file6
23# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6
24# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/file7
25# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7
26# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file8
27# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8
28# 11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file9
29# 0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9
30my ($dir) = {};
31my ($root) = {"start" => $dir};
32for (my ($i) = 0; $i < 10; $i++) {
33 $dir->{"file$i"} = ["contents $i\n"];
34 $dir = $dir->{"dir$i"} = {};
35}
36check_archive ($root);
37pass;
diff --git a/pintos-progos/tests/filesys/extended/dir-vine.c b/pintos-progos/tests/filesys/extended/dir-vine.c
new file mode 100644
index 0000000..8a31c38
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-vine.c
@@ -0,0 +1,85 @@
1/* Create a very deep "vine" of directories: /dir0/dir1/dir2/...
2 and an ordinary file in each of them, until we fill up the
3 disk.
4
5 Then delete most of them, for two reasons. First, "tar"
6 limits file names to 100 characters (which could be extended
7 to 256 without much trouble). Second, a full disk has no room
8 for the tar archive. */
9
10#include <string.h>
11#include <stdio.h>
12#include <syscall.h>
13#include "tests/lib.h"
14#include "tests/main.h"
15
16void
17test_main (void)
18{
19 int i;
20
21 msg ("creating many levels of files and directories...");
22 quiet = true;
23 CHECK (mkdir ("start"), "mkdir \"start\"");
24 CHECK (chdir ("start"), "chdir \"start\"");
25 for (i = 0; ; i++)
26 {
27 char name[3][READDIR_MAX_LEN + 1];
28 char file_name[16], dir_name[16];
29 char contents[128];
30 int fd;
31
32 /* Create file. */
33 snprintf (file_name, sizeof file_name, "file%d", i);
34 if (!create (file_name, 0))
35 break;
36 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
37 snprintf (contents, sizeof contents, "contents %d\n", i);
38 if (write (fd, contents, strlen (contents)) != (int) strlen (contents))
39 {
40 CHECK (remove (file_name), "remove \"%s\"", file_name);
41 close (fd);
42 break;
43 }
44 close (fd);
45
46 /* Create directory. */
47 snprintf (dir_name, sizeof dir_name, "dir%d", i);
48 if (!mkdir (dir_name))
49 {
50 CHECK (remove (file_name), "remove \"%s\"", file_name);
51 break;
52 }
53
54 /* Check for file and directory. */
55 CHECK ((fd = open (".")) > 1, "open \".\"");
56 CHECK (readdir (fd, name[0]), "readdir \".\"");
57 CHECK (readdir (fd, name[1]), "readdir \".\"");
58 CHECK (!readdir (fd, name[2]), "readdir \".\" (should fail)");
59 CHECK ((!strcmp (name[0], dir_name) && !strcmp (name[1], file_name))
60 || (!strcmp (name[1], dir_name) && !strcmp (name[0], file_name)),
61 "names should be \"%s\" and \"%s\", "
62 "actually \"%s\" and \"%s\"",
63 file_name, dir_name, name[0], name[1]);
64 close (fd);
65
66 /* Descend into directory. */
67 CHECK (chdir (dir_name), "chdir \"%s\"", dir_name);
68 }
69 CHECK (i > 200, "created files and directories only to level %d", i);
70 quiet = false;
71
72 msg ("removing all but top 10 levels of files and directories...");
73 quiet = true;
74 while (i-- > 10)
75 {
76 char file_name[16], dir_name[16];
77
78 snprintf (file_name, sizeof file_name, "file%d", i);
79 snprintf (dir_name, sizeof dir_name, "dir%d", i);
80 CHECK (chdir (".."), "chdir \"..\"");
81 CHECK (remove (dir_name), "remove \"%s\"", dir_name);
82 CHECK (remove (file_name), "remove \"%s\"", file_name);
83 }
84 quiet = false;
85}
diff --git a/pintos-progos/tests/filesys/extended/dir-vine.ck b/pintos-progos/tests/filesys/extended/dir-vine.ck
new file mode 100644
index 0000000..db452b0
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/dir-vine.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(dir-vine) begin
7(dir-vine) creating many levels of files and directories...
8(dir-vine) removing all but top 10 levels of files and directories...
9(dir-vine) end
10EOF
11pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-create-persistence.ck b/pintos-progos/tests/filesys/extended/grow-create-persistence.ck
new file mode 100644
index 0000000..bbcb24f
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-create-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"blargle" => ['']});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-create.c b/pintos-progos/tests/filesys/extended/grow-create.c
new file mode 100644
index 0000000..9ccc4ea
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-create.c
@@ -0,0 +1,4 @@
1/* Create a file of size 0. */
2
3#define TEST_SIZE 0
4#include "tests/filesys/create.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-create.ck b/pintos-progos/tests/filesys/extended/grow-create.ck
new file mode 100644
index 0000000..b2e69d1
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-create.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(grow-create) begin
7(grow-create) create "blargle"
8(grow-create) open "blargle" for verification
9(grow-create) verified contents of "blargle"
10(grow-create) close "blargle"
11(grow-create) end
12EOF
13pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck
new file mode 100644
index 0000000..989a322
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-dir-lg-persistence.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6my ($fs);
7$fs->{'x'}{"file$_"} = [random_bytes (512)] foreach 0...49;
8check_archive ($fs);
9pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg.c b/pintos-progos/tests/filesys/extended/grow-dir-lg.c
new file mode 100644
index 0000000..20a194b
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-dir-lg.c
@@ -0,0 +1,6 @@
1/* Creates a directory,
2 then creates 50 files in that directory. */
3
4#define FILE_CNT 50
5#define DIRECTORY "/x"
6#include "tests/filesys/extended/grow-dir.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-dir-lg.ck b/pintos-progos/tests/filesys/extended/grow-dir-lg.ck
new file mode 100644
index 0000000..ec58bd3
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-dir-lg.ck
@@ -0,0 +1,61 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-dir-lg) begin
8(grow-dir-lg) mkdir /x
9(grow-dir-lg) creating and checking "/x/file0"
10(grow-dir-lg) creating and checking "/x/file1"
11(grow-dir-lg) creating and checking "/x/file2"
12(grow-dir-lg) creating and checking "/x/file3"
13(grow-dir-lg) creating and checking "/x/file4"
14(grow-dir-lg) creating and checking "/x/file5"
15(grow-dir-lg) creating and checking "/x/file6"
16(grow-dir-lg) creating and checking "/x/file7"
17(grow-dir-lg) creating and checking "/x/file8"
18(grow-dir-lg) creating and checking "/x/file9"
19(grow-dir-lg) creating and checking "/x/file10"
20(grow-dir-lg) creating and checking "/x/file11"
21(grow-dir-lg) creating and checking "/x/file12"
22(grow-dir-lg) creating and checking "/x/file13"
23(grow-dir-lg) creating and checking "/x/file14"
24(grow-dir-lg) creating and checking "/x/file15"
25(grow-dir-lg) creating and checking "/x/file16"
26(grow-dir-lg) creating and checking "/x/file17"
27(grow-dir-lg) creating and checking "/x/file18"
28(grow-dir-lg) creating and checking "/x/file19"
29(grow-dir-lg) creating and checking "/x/file20"
30(grow-dir-lg) creating and checking "/x/file21"
31(grow-dir-lg) creating and checking "/x/file22"
32(grow-dir-lg) creating and checking "/x/file23"
33(grow-dir-lg) creating and checking "/x/file24"
34(grow-dir-lg) creating and checking "/x/file25"
35(grow-dir-lg) creating and checking "/x/file26"
36(grow-dir-lg) creating and checking "/x/file27"
37(grow-dir-lg) creating and checking "/x/file28"
38(grow-dir-lg) creating and checking "/x/file29"
39(grow-dir-lg) creating and checking "/x/file30"
40(grow-dir-lg) creating and checking "/x/file31"
41(grow-dir-lg) creating and checking "/x/file32"
42(grow-dir-lg) creating and checking "/x/file33"
43(grow-dir-lg) creating and checking "/x/file34"
44(grow-dir-lg) creating and checking "/x/file35"
45(grow-dir-lg) creating and checking "/x/file36"
46(grow-dir-lg) creating and checking "/x/file37"
47(grow-dir-lg) creating and checking "/x/file38"
48(grow-dir-lg) creating and checking "/x/file39"
49(grow-dir-lg) creating and checking "/x/file40"
50(grow-dir-lg) creating and checking "/x/file41"
51(grow-dir-lg) creating and checking "/x/file42"
52(grow-dir-lg) creating and checking "/x/file43"
53(grow-dir-lg) creating and checking "/x/file44"
54(grow-dir-lg) creating and checking "/x/file45"
55(grow-dir-lg) creating and checking "/x/file46"
56(grow-dir-lg) creating and checking "/x/file47"
57(grow-dir-lg) creating and checking "/x/file48"
58(grow-dir-lg) creating and checking "/x/file49"
59(grow-dir-lg) end
60EOF
61pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-dir.inc b/pintos-progos/tests/filesys/extended/grow-dir.inc
new file mode 100644
index 0000000..bee0ba0
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-dir.inc
@@ -0,0 +1,41 @@
1/* -*- c -*- */
2
3#include <syscall.h>
4#include <stdio.h>
5#include "tests/filesys/seq-test.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9static char buf[512];
10
11static size_t
12return_block_size (void)
13{
14 return sizeof buf;
15}
16
17void
18test_main (void)
19{
20 size_t i;
21
22#ifdef DIRECTORY
23 CHECK (mkdir (DIRECTORY), "mkdir %s", DIRECTORY);
24#define DIR_PREFIX DIRECTORY "/"
25#else
26#define DIR_PREFIX ""
27#endif
28 for (i = 0; i < FILE_CNT; i++)
29 {
30 char file_name[128];
31 snprintf (file_name, sizeof file_name, "%sfile%zu", DIR_PREFIX, i);
32
33 msg ("creating and checking \"%s\"", file_name);
34
35 quiet = true;
36 seq_test (file_name,
37 buf, sizeof buf, sizeof buf,
38 return_block_size, NULL);
39 quiet = false;
40 }
41}
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck b/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck
new file mode 100644
index 0000000..150f383
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-file-size-persistence.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_archive ({"testfile" => [random_bytes (2134)]});
7pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size.c b/pintos-progos/tests/filesys/extended/grow-file-size.c
new file mode 100644
index 0000000..3ce8588
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-file-size.c
@@ -0,0 +1,33 @@
1/* Grows a file from 0 bytes to 2,134 bytes, 37 bytes at a time,
2 and checks that the file's size is reported correctly at each
3 step. */
4
5#include <syscall.h>
6#include "tests/filesys/seq-test.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10static char buf[2134];
11
12static size_t
13return_block_size (void)
14{
15 return 37;
16}
17
18static void
19check_file_size (int fd, long ofs)
20{
21 long size = filesize (fd);
22 if (size != ofs)
23 fail ("filesize not updated properly: should be %ld, actually %ld",
24 ofs, size);
25}
26
27void
28test_main (void)
29{
30 seq_test ("testfile",
31 buf, sizeof buf, 0,
32 return_block_size, check_file_size);
33}
diff --git a/pintos-progos/tests/filesys/extended/grow-file-size.ck b/pintos-progos/tests/filesys/extended/grow-file-size.ck
new file mode 100644
index 0000000..d81feff
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-file-size.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-file-size) begin
8(grow-file-size) create "testfile"
9(grow-file-size) open "testfile"
10(grow-file-size) writing "testfile"
11(grow-file-size) close "testfile"
12(grow-file-size) open "testfile" for verification
13(grow-file-size) verified contents of "testfile"
14(grow-file-size) close "testfile"
15(grow-file-size) end
16EOF
17pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck
new file mode 100644
index 0000000..1692f46
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-lg-persistence.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6my ($fs);
7$fs->{"file$_"} = [random_bytes (512)] foreach 0...49;
8check_archive ($fs);
9pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg.c b/pintos-progos/tests/filesys/extended/grow-root-lg.c
new file mode 100644
index 0000000..d8d6c09
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-lg.c
@@ -0,0 +1,4 @@
1/* Creates 50 files in the root directory. */
2
3#define FILE_CNT 50
4#include "tests/filesys/extended/grow-dir.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-root-lg.ck b/pintos-progos/tests/filesys/extended/grow-root-lg.ck
new file mode 100644
index 0000000..b174bc9
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-lg.ck
@@ -0,0 +1,60 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-root-lg) begin
8(grow-root-lg) creating and checking "file0"
9(grow-root-lg) creating and checking "file1"
10(grow-root-lg) creating and checking "file2"
11(grow-root-lg) creating and checking "file3"
12(grow-root-lg) creating and checking "file4"
13(grow-root-lg) creating and checking "file5"
14(grow-root-lg) creating and checking "file6"
15(grow-root-lg) creating and checking "file7"
16(grow-root-lg) creating and checking "file8"
17(grow-root-lg) creating and checking "file9"
18(grow-root-lg) creating and checking "file10"
19(grow-root-lg) creating and checking "file11"
20(grow-root-lg) creating and checking "file12"
21(grow-root-lg) creating and checking "file13"
22(grow-root-lg) creating and checking "file14"
23(grow-root-lg) creating and checking "file15"
24(grow-root-lg) creating and checking "file16"
25(grow-root-lg) creating and checking "file17"
26(grow-root-lg) creating and checking "file18"
27(grow-root-lg) creating and checking "file19"
28(grow-root-lg) creating and checking "file20"
29(grow-root-lg) creating and checking "file21"
30(grow-root-lg) creating and checking "file22"
31(grow-root-lg) creating and checking "file23"
32(grow-root-lg) creating and checking "file24"
33(grow-root-lg) creating and checking "file25"
34(grow-root-lg) creating and checking "file26"
35(grow-root-lg) creating and checking "file27"
36(grow-root-lg) creating and checking "file28"
37(grow-root-lg) creating and checking "file29"
38(grow-root-lg) creating and checking "file30"
39(grow-root-lg) creating and checking "file31"
40(grow-root-lg) creating and checking "file32"
41(grow-root-lg) creating and checking "file33"
42(grow-root-lg) creating and checking "file34"
43(grow-root-lg) creating and checking "file35"
44(grow-root-lg) creating and checking "file36"
45(grow-root-lg) creating and checking "file37"
46(grow-root-lg) creating and checking "file38"
47(grow-root-lg) creating and checking "file39"
48(grow-root-lg) creating and checking "file40"
49(grow-root-lg) creating and checking "file41"
50(grow-root-lg) creating and checking "file42"
51(grow-root-lg) creating and checking "file43"
52(grow-root-lg) creating and checking "file44"
53(grow-root-lg) creating and checking "file45"
54(grow-root-lg) creating and checking "file46"
55(grow-root-lg) creating and checking "file47"
56(grow-root-lg) creating and checking "file48"
57(grow-root-lg) creating and checking "file49"
58(grow-root-lg) end
59EOF
60pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck b/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck
new file mode 100644
index 0000000..2b0b8ab
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-sm-persistence.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6my ($fs);
7$fs->{"file$_"} = [random_bytes (512)] foreach 0...19;
8check_archive ($fs);
9pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm.c b/pintos-progos/tests/filesys/extended/grow-root-sm.c
new file mode 100644
index 0000000..ee375d5
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-sm.c
@@ -0,0 +1,4 @@
1/* Creates 20 files in the root directory. */
2
3#define FILE_CNT 20
4#include "tests/filesys/extended/grow-dir.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-root-sm.ck b/pintos-progos/tests/filesys/extended/grow-root-sm.ck
new file mode 100644
index 0000000..1aac7e9
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-root-sm.ck
@@ -0,0 +1,30 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-root-sm) begin
8(grow-root-sm) creating and checking "file0"
9(grow-root-sm) creating and checking "file1"
10(grow-root-sm) creating and checking "file2"
11(grow-root-sm) creating and checking "file3"
12(grow-root-sm) creating and checking "file4"
13(grow-root-sm) creating and checking "file5"
14(grow-root-sm) creating and checking "file6"
15(grow-root-sm) creating and checking "file7"
16(grow-root-sm) creating and checking "file8"
17(grow-root-sm) creating and checking "file9"
18(grow-root-sm) creating and checking "file10"
19(grow-root-sm) creating and checking "file11"
20(grow-root-sm) creating and checking "file12"
21(grow-root-sm) creating and checking "file13"
22(grow-root-sm) creating and checking "file14"
23(grow-root-sm) creating and checking "file15"
24(grow-root-sm) creating and checking "file16"
25(grow-root-sm) creating and checking "file17"
26(grow-root-sm) creating and checking "file18"
27(grow-root-sm) creating and checking "file19"
28(grow-root-sm) end
29EOF
30pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck b/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck
new file mode 100644
index 0000000..41aaae0
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-lg-persistence.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_archive ({"testme" => [random_bytes (72943)]});
7pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg.c b/pintos-progos/tests/filesys/extended/grow-seq-lg.c
new file mode 100644
index 0000000..3108d17
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-lg.c
@@ -0,0 +1,5 @@
1/* Grows a file from 0 bytes to 72,943 bytes, 1,234 bytes at a
2 time. */
3
4#define TEST_SIZE 72943
5#include "tests/filesys/extended/grow-seq.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-lg.ck b/pintos-progos/tests/filesys/extended/grow-seq-lg.ck
new file mode 100644
index 0000000..90fcd8c
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-lg.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-seq-lg) begin
8(grow-seq-lg) create "testme"
9(grow-seq-lg) open "testme"
10(grow-seq-lg) writing "testme"
11(grow-seq-lg) close "testme"
12(grow-seq-lg) open "testme" for verification
13(grow-seq-lg) verified contents of "testme"
14(grow-seq-lg) close "testme"
15(grow-seq-lg) end
16EOF
17pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck b/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck
new file mode 100644
index 0000000..6cb0bd8
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-sm-persistence.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_archive ({"testme" => [random_bytes (5678)]});
7pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm.c b/pintos-progos/tests/filesys/extended/grow-seq-sm.c
new file mode 100644
index 0000000..3656e2e
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-sm.c
@@ -0,0 +1,5 @@
1/* Grows a file from 0 bytes to 5,678 bytes, 1,234 bytes at a
2 time. */
3
4#define TEST_SIZE 5678
5#include "tests/filesys/extended/grow-seq.inc"
diff --git a/pintos-progos/tests/filesys/extended/grow-seq-sm.ck b/pintos-progos/tests/filesys/extended/grow-seq-sm.ck
new file mode 100644
index 0000000..5cf4518
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq-sm.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-seq-sm) begin
8(grow-seq-sm) create "testme"
9(grow-seq-sm) open "testme"
10(grow-seq-sm) writing "testme"
11(grow-seq-sm) close "testme"
12(grow-seq-sm) open "testme" for verification
13(grow-seq-sm) verified contents of "testme"
14(grow-seq-sm) close "testme"
15(grow-seq-sm) end
16EOF
17pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-seq.inc b/pintos-progos/tests/filesys/extended/grow-seq.inc
new file mode 100644
index 0000000..1b7710c
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-seq.inc
@@ -0,0 +1,20 @@
1/* -*- c -*- */
2
3#include "tests/filesys/seq-test.h"
4#include "tests/main.h"
5
6static char buf[TEST_SIZE];
7
8static size_t
9return_block_size (void)
10{
11 return 1234;
12}
13
14void
15test_main (void)
16{
17 seq_test ("testme",
18 buf, sizeof buf, 0,
19 return_block_size, NULL);
20}
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck b/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck
new file mode 100644
index 0000000..3f06a5b
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-sparse-persistence.ck
@@ -0,0 +1,6 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_archive ({"testfile" => ["\0" x 76543]});
6pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse.c b/pintos-progos/tests/filesys/extended/grow-sparse.c
new file mode 100644
index 0000000..6eab210
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-sparse.c
@@ -0,0 +1,25 @@
1/* Tests that seeking past the end of a file and writing will
2 properly zero out the region in between. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8static char buf[76543];
9
10void
11test_main (void)
12{
13 const char *file_name = "testfile";
14 char zero = 0;
15 int fd;
16
17 CHECK (create (file_name, 0), "create \"%s\"", file_name);
18 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
19 msg ("seek \"%s\"", file_name);
20 seek (fd, sizeof buf - 1);
21 CHECK (write (fd, &zero, 1) > 0, "write \"%s\"", file_name);
22 msg ("close \"%s\"", file_name);
23 close (fd);
24 check_file (file_name, buf, sizeof buf);
25}
diff --git a/pintos-progos/tests/filesys/extended/grow-sparse.ck b/pintos-progos/tests/filesys/extended/grow-sparse.ck
new file mode 100644
index 0000000..379ba2c
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-sparse.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(grow-sparse) begin
7(grow-sparse) create "testfile"
8(grow-sparse) open "testfile"
9(grow-sparse) seek "testfile"
10(grow-sparse) write "testfile"
11(grow-sparse) close "testfile"
12(grow-sparse) open "testfile" for verification
13(grow-sparse) verified contents of "testfile"
14(grow-sparse) close "testfile"
15(grow-sparse) end
16EOF
17pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck b/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck
new file mode 100644
index 0000000..d93a422
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-tell-persistence.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_archive ({"foobar" => [random_bytes (2134)]});
7pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-tell.c b/pintos-progos/tests/filesys/extended/grow-tell.c
new file mode 100644
index 0000000..5f5da5b
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-tell.c
@@ -0,0 +1,32 @@
1/* Checks that growing a file updates the file position
2 correctly. */
3
4#include <syscall.h>
5#include "tests/filesys/seq-test.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9static char buf[2134];
10
11static size_t
12return_block_size (void)
13{
14 return 37;
15}
16
17static void
18check_tell (int fd, long ofs)
19{
20 long pos = tell (fd);
21 if (pos != ofs)
22 fail ("file position not updated properly: should be %ld, actually %ld",
23 ofs, pos);
24}
25
26void
27test_main (void)
28{
29 seq_test ("foobar",
30 buf, sizeof buf, 0,
31 return_block_size, check_tell);
32}
diff --git a/pintos-progos/tests/filesys/extended/grow-tell.ck b/pintos-progos/tests/filesys/extended/grow-tell.ck
new file mode 100644
index 0000000..fe94707
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-tell.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-tell) begin
8(grow-tell) create "foobar"
9(grow-tell) open "foobar"
10(grow-tell) writing "foobar"
11(grow-tell) close "foobar"
12(grow-tell) open "foobar" for verification
13(grow-tell) verified contents of "foobar"
14(grow-tell) close "foobar"
15(grow-tell) end
16EOF
17pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck b/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck
new file mode 100644
index 0000000..1c4ced1
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-two-files-persistence.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6my ($a) = random_bytes (8143);
7my ($b) = random_bytes (8143);
8check_archive ({"a" => [$a], "b" => [$b]});
9pass;
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files.c b/pintos-progos/tests/filesys/extended/grow-two-files.c
new file mode 100644
index 0000000..6a8fb1c
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-two-files.c
@@ -0,0 +1,62 @@
1/* Grows two files in parallel and checks that their contents are
2 correct. */
3
4#include <random.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9#define FILE_SIZE 8143
10static char buf_a[FILE_SIZE];
11static char buf_b[FILE_SIZE];
12
13static void
14write_some_bytes (const char *file_name, int fd, const char *buf, size_t *ofs)
15{
16 if (*ofs < FILE_SIZE)
17 {
18 size_t block_size = random_ulong () % (FILE_SIZE / 8) + 1;
19 size_t ret_val;
20 if (block_size > FILE_SIZE - *ofs)
21 block_size = FILE_SIZE - *ofs;
22
23 ret_val = write (fd, buf + *ofs, block_size);
24 if (ret_val != block_size)
25 fail ("write %zu bytes at offset %zu in \"%s\" returned %zu",
26 block_size, *ofs, file_name, ret_val);
27 *ofs += block_size;
28 }
29}
30
31void
32test_main (void)
33{
34 int fd_a, fd_b;
35 size_t ofs_a = 0, ofs_b = 0;
36
37 random_init (0);
38 random_bytes (buf_a, sizeof buf_a);
39 random_bytes (buf_b, sizeof buf_b);
40
41 CHECK (create ("a", 0), "create \"a\"");
42 CHECK (create ("b", 0), "create \"b\"");
43
44 CHECK ((fd_a = open ("a")) > 1, "open \"a\"");
45 CHECK ((fd_b = open ("b")) > 1, "open \"b\"");
46
47 msg ("write \"a\" and \"b\" alternately");
48 while (ofs_a < FILE_SIZE || ofs_b < FILE_SIZE)
49 {
50 write_some_bytes ("a", fd_a, buf_a, &ofs_a);
51 write_some_bytes ("b", fd_b, buf_b, &ofs_b);
52 }
53
54 msg ("close \"a\"");
55 close (fd_a);
56
57 msg ("close \"b\"");
58 close (fd_b);
59
60 check_file ("a", buf_a, FILE_SIZE);
61 check_file ("b", buf_b, FILE_SIZE);
62}
diff --git a/pintos-progos/tests/filesys/extended/grow-two-files.ck b/pintos-progos/tests/filesys/extended/grow-two-files.ck
new file mode 100644
index 0000000..b5e754a
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/grow-two-files.ck
@@ -0,0 +1,23 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(grow-two-files) begin
8(grow-two-files) create "a"
9(grow-two-files) create "b"
10(grow-two-files) open "a"
11(grow-two-files) open "b"
12(grow-two-files) write "a" and "b" alternately
13(grow-two-files) close "a"
14(grow-two-files) close "b"
15(grow-two-files) open "a" for verification
16(grow-two-files) verified contents of "a"
17(grow-two-files) close "a"
18(grow-two-files) open "b" for verification
19(grow-two-files) verified contents of "b"
20(grow-two-files) close "b"
21(grow-two-files) end
22EOF
23pass;
diff --git a/pintos-progos/tests/filesys/extended/mk-tree.c b/pintos-progos/tests/filesys/extended/mk-tree.c
new file mode 100644
index 0000000..a36bb88
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/mk-tree.c
@@ -0,0 +1,67 @@
1/* Library function for creating a tree of directories. */
2
3#include <stdio.h>
4#include <syscall.h>
5#include "tests/filesys/extended/mk-tree.h"
6#include "tests/lib.h"
7
8static void do_mkdir (const char *format, ...) PRINTF_FORMAT (1, 2);
9static void do_touch (const char *format, ...) PRINTF_FORMAT (1, 2);
10
11void
12make_tree (int at, int bt, int ct, int dt)
13{
14 char try[128];
15 int a, b, c, d;
16 int fd;
17
18 msg ("creating /0/0/0/0 through /%d/%d/%d/%d...",
19 at - 1, bt - 1, ct - 1, dt - 1);
20 quiet = true;
21 for (a = 0; a < at; a++)
22 {
23 do_mkdir ("/%d", a);
24 for (b = 0; b < bt; b++)
25 {
26 do_mkdir ("/%d/%d", a, b);
27 for (c = 0; c < ct; c++)
28 {
29 do_mkdir ("/%d/%d/%d", a, b, c);
30 for (d = 0; d < dt; d++)
31 do_touch ("/%d/%d/%d/%d", a, b, c, d);
32 }
33 }
34 }
35 quiet = false;
36
37 snprintf (try, sizeof try, "/%d/%d/%d/%d", 0, bt - 1, 0, dt - 1);
38 CHECK ((fd = open (try)) > 1, "open \"%s\"", try);
39 msg ("close \"%s\"", try);
40 close (fd);
41}
42
43static void
44do_mkdir (const char *format, ...)
45{
46 char dir[128];
47 va_list args;
48
49 va_start (args, format);
50 vsnprintf (dir, sizeof dir, format, args);
51 va_end (args);
52
53 CHECK (mkdir (dir), "mkdir \"%s\"", dir);
54}
55
56static void
57do_touch (const char *format, ...)
58{
59 char file[128];
60 va_list args;
61
62 va_start (args, format);
63 vsnprintf (file, sizeof file, format, args);
64 va_end (args);
65
66 CHECK (create (file, 0), "create \"%s\"", file);
67}
diff --git a/pintos-progos/tests/filesys/extended/mk-tree.h b/pintos-progos/tests/filesys/extended/mk-tree.h
new file mode 100644
index 0000000..df0d5a6
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/mk-tree.h
@@ -0,0 +1,6 @@
1#ifndef TESTS_FILESYS_EXTENDED_MK_TREE_H
2#define TESTS_FILESYS_EXTENDED_MK_TREE_H
3
4void make_tree (int at, int bt, int ct, int dt);
5
6#endif /* tests/filesys/extended/mk-tree.h */
diff --git a/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck b/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck
new file mode 100644
index 0000000..62d57ee
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/syn-rw-persistence.ck
@@ -0,0 +1,8 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_archive ({"child-syn-rw" => "tests/filesys/extended/child-syn-rw",
7 "logfile" => [random_bytes (8 * 512)]});
8pass;
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.c b/pintos-progos/tests/filesys/extended/syn-rw.c
new file mode 100644
index 0000000..657dfb5
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/syn-rw.c
@@ -0,0 +1,35 @@
1/* Grows a file in chunks while subprocesses read the growing
2 file. */
3
4#include <random.h>
5#include <syscall.h>
6#include "tests/filesys/extended/syn-rw.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10char buf[BUF_SIZE];
11
12#define CHILD_CNT 4
13
14void
15test_main (void)
16{
17 pid_t children[CHILD_CNT];
18 size_t ofs;
19 int fd;
20
21 CHECK (create (file_name, 0), "create \"%s\"", file_name);
22 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
23
24 exec_children ("child-syn-rw", children, CHILD_CNT);
25
26 random_bytes (buf, sizeof buf);
27 quiet = true;
28 for (ofs = 0; ofs < BUF_SIZE; ofs += CHUNK_SIZE)
29 CHECK (write (fd, buf + ofs, CHUNK_SIZE) > 0,
30 "write %d bytes at offset %zu in \"%s\"",
31 (int) CHUNK_SIZE, ofs, file_name);
32 quiet = false;
33
34 wait_children (children, CHILD_CNT);
35}
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.ck b/pintos-progos/tests/filesys/extended/syn-rw.ck
new file mode 100644
index 0000000..ac82aa8
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/syn-rw.ck
@@ -0,0 +1,20 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::random;
6check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
7(syn-rw) begin
8(syn-rw) create "logfile"
9(syn-rw) open "logfile"
10(syn-rw) exec child 1 of 4: "child-syn-rw 0"
11(syn-rw) exec child 2 of 4: "child-syn-rw 1"
12(syn-rw) exec child 3 of 4: "child-syn-rw 2"
13(syn-rw) exec child 4 of 4: "child-syn-rw 3"
14(syn-rw) wait for child 1 of 4 returned 0 (expected 0)
15(syn-rw) wait for child 2 of 4 returned 1 (expected 1)
16(syn-rw) wait for child 3 of 4 returned 2 (expected 2)
17(syn-rw) wait for child 4 of 4 returned 3 (expected 3)
18(syn-rw) end
19EOF
20pass;
diff --git a/pintos-progos/tests/filesys/extended/syn-rw.h b/pintos-progos/tests/filesys/extended/syn-rw.h
new file mode 100644
index 0000000..170aeb4
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/syn-rw.h
@@ -0,0 +1,9 @@
1#ifndef TESTS_FILESYS_EXTENDED_SYN_RW_H
2#define TESTS_FILESYS_EXTENDED_SYN_RW_H
3
4#define CHUNK_SIZE 8
5#define CHUNK_CNT 512
6#define BUF_SIZE (CHUNK_SIZE * CHUNK_CNT)
7static const char file_name[] = "logfile";
8
9#endif /* tests/filesys/extended/syn-rw.h */
diff --git a/pintos-progos/tests/filesys/extended/tar.c b/pintos-progos/tests/filesys/extended/tar.c
new file mode 100644
index 0000000..9801484
--- /dev/null
+++ b/pintos-progos/tests/filesys/extended/tar.c
@@ -0,0 +1,208 @@
1/* tar.c
2
3 Creates a tar archive. */
4
5#include <ustar.h>
6#include <syscall.h>
7#include <stdio.h>
8#include <string.h>
9
10static void usage (void);
11static bool make_tar_archive (const char *archive_name,
12 char *files[], size_t file_cnt);
13
14int
15main (int argc, char *argv[])
16{
17 if (argc < 3)
18 usage ();
19
20 return (make_tar_archive (argv[1], argv + 2, argc - 2)
21 ? EXIT_SUCCESS : EXIT_FAILURE);
22}
23
24static void
25usage (void)
26{
27 printf ("tar, tar archive creator\n"
28 "Usage: tar ARCHIVE FILE...\n"
29 "where ARCHIVE is the tar archive to create\n"
30 " and FILE... is a list of files or directories to put into it.\n"
31 "(ARCHIVE itself will not be included in the archive, even if it\n"
32 "is in a directory to be archived.)\n");
33 exit (EXIT_FAILURE);
34}
35
36static bool archive_file (char file_name[], size_t file_name_size,
37 int archive_fd, bool *write_error);
38
39static bool archive_ordinary_file (const char *file_name, int file_fd,
40 int archive_fd, bool *write_error);
41static bool archive_directory (char file_name[], size_t file_name_size,
42 int file_fd, int archive_fd, bool *write_error);
43static bool write_header (const char *file_name, enum ustar_type, int size,
44 int archive_fd, bool *write_error);
45
46static bool do_write (int fd, const char *buffer, int size, bool *write_error);
47
48static bool
49make_tar_archive (const char *archive_name, char *files[], size_t file_cnt)
50{
51 static const char zeros[512];
52 int archive_fd;
53 bool success = true;
54 bool write_error = false;
55 size_t i;
56
57 if (!create (archive_name, 0))
58 {
59 printf ("%s: create failed\n", archive_name);
60 return false;
61 }
62 archive_fd = open (archive_name);
63 if (archive_fd < 0)
64 {
65 printf ("%s: open failed\n", archive_name);
66 return false;
67 }
68
69 for (i = 0; i < file_cnt; i++)
70 {
71 char file_name[128];
72
73 strlcpy (file_name, files[i], sizeof file_name);
74 if (!archive_file (file_name, sizeof file_name,
75 archive_fd, &write_error))
76 success = false;
77 }
78
79 if (!do_write (archive_fd, zeros, 512, &write_error)
80 || !do_write (archive_fd, zeros, 512, &write_error))
81 success = false;
82
83 close (archive_fd);
84
85 return success;
86}
87
88static bool
89archive_file (char file_name[], size_t file_name_size,
90 int archive_fd, bool *write_error)
91{
92 int file_fd = open (file_name);
93 if (file_fd >= 0)
94 {
95 bool success;
96
97 if (inumber (file_fd) != inumber (archive_fd))
98 {
99 if (!isdir (file_fd))
100 success = archive_ordinary_file (file_name, file_fd,
101 archive_fd, write_error);
102 else
103 success = archive_directory (file_name, file_name_size, file_fd,
104 archive_fd, write_error);
105 }
106 else
107 {
108 /* Nothing to do: don't try to archive the archive file. */
109 success = true;
110 }
111
112 close (file_fd);
113
114 return success;
115 }
116 else
117 {
118 printf ("%s: open failed\n", file_name);
119 return false;
120 }
121}
122
123static bool
124archive_ordinary_file (const char *file_name, int file_fd,
125 int archive_fd, bool *write_error)
126{
127 bool read_error = false;
128 bool success = true;
129 int file_size = filesize (file_fd);
130
131 if (!write_header (file_name, USTAR_REGULAR, file_size,
132 archive_fd, write_error))
133 return false;
134
135 while (file_size > 0)
136 {
137 static char buf[512];
138 int chunk_size = file_size > 512 ? 512 : file_size;
139 int read_retval = read (file_fd, buf, chunk_size);
140 int bytes_read = read_retval > 0 ? read_retval : 0;
141
142 if (bytes_read != chunk_size && !read_error)
143 {
144 printf ("%s: read error\n", file_name);
145 read_error = true;
146 success = false;
147 }
148
149 memset (buf + bytes_read, 0, 512 - bytes_read);
150 if (!do_write (archive_fd, buf, 512, write_error))
151 success = false;
152
153 file_size -= chunk_size;
154 }
155
156 return success;
157}
158
159static bool
160archive_directory (char file_name[], size_t file_name_size, int file_fd,
161 int archive_fd, bool *write_error)
162{
163 size_t dir_len;
164 bool success = true;
165
166 dir_len = strlen (file_name);
167 if (dir_len + 1 + READDIR_MAX_LEN + 1 > file_name_size)
168 {
169 printf ("%s: file name too long\n", file_name);
170 return false;
171 }
172
173 if (!write_header (file_name, USTAR_DIRECTORY, 0, archive_fd, write_error))
174 return false;
175
176 file_name[dir_len] = '/';
177 while (readdir (file_fd, &file_name[dir_len + 1]))
178 if (!archive_file (file_name, file_name_size, archive_fd, write_error))
179 success = false;
180 file_name[dir_len] = '\0';
181
182 return success;
183}
184
185static bool
186write_header (const char *file_name, enum ustar_type type, int size,
187 int archive_fd, bool *write_error)
188{
189 static char header[512];
190 return (ustar_make_header (file_name, type, size, header)
191 && do_write (archive_fd, header, 512, write_error));
192}
193
194static bool
195do_write (int fd, const char *buffer, int size, bool *write_error)
196{
197 if (write (fd, buffer, size) == size)
198 return true;
199 else
200 {
201 if (!*write_error)
202 {
203 printf ("error writing archive\n");
204 *write_error = true;
205 }
206 return false;
207 }
208}
diff --git a/pintos-progos/tests/filesys/seq-test.c b/pintos-progos/tests/filesys/seq-test.c
new file mode 100644
index 0000000..8ce222c
--- /dev/null
+++ b/pintos-progos/tests/filesys/seq-test.c
@@ -0,0 +1,37 @@
1#include "tests/filesys/seq-test.h"
2#include <random.h>
3#include <syscall.h>
4#include "tests/lib.h"
5
6void
7seq_test (const char *file_name, void *buf, size_t size, size_t initial_size,
8 size_t (*block_size_func) (void),
9 void (*check_func) (int fd, long ofs))
10{
11 size_t ofs;
12 int fd;
13
14 random_bytes (buf, size);
15 CHECK (create (file_name, initial_size), "create \"%s\"", file_name);
16 CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
17
18 ofs = 0;
19 msg ("writing \"%s\"", file_name);
20 while (ofs < size)
21 {
22 size_t block_size = block_size_func ();
23 if (block_size > size - ofs)
24 block_size = size - ofs;
25
26 if (write (fd, buf + ofs, block_size) != (int) block_size)
27 fail ("write %zu bytes at offset %zu in \"%s\" failed",
28 block_size, ofs, file_name);
29
30 ofs += block_size;
31 if (check_func != NULL)
32 check_func (fd, ofs);
33 }
34 msg ("close \"%s\"", file_name);
35 close (fd);
36 check_file (file_name, buf, size);
37}
diff --git a/pintos-progos/tests/filesys/seq-test.h b/pintos-progos/tests/filesys/seq-test.h
new file mode 100644
index 0000000..0697381
--- /dev/null
+++ b/pintos-progos/tests/filesys/seq-test.h
@@ -0,0 +1,11 @@
1#ifndef TESTS_FILESYS_SEQ_TEST_H
2#define TESTS_FILESYS_SEQ_TEST_H
3
4#include <stddef.h>
5
6void seq_test (const char *file_name,
7 void *buf, size_t size, size_t initial_size,
8 size_t (*block_size_func) (void),
9 void (*check_func) (int fd, long ofs));
10
11#endif /* tests/filesys/seq-test.h */
diff --git a/pintos-progos/tests/internal/list.c b/pintos-progos/tests/internal/list.c
new file mode 100644
index 0000000..836c69e
--- /dev/null
+++ b/pintos-progos/tests/internal/list.c
@@ -0,0 +1,174 @@
1/* Test program for lib/kernel/list.c.
2
3 Attempts to test the list functionality that is not
4 sufficiently tested elsewhere in Pintos.
5
6 This is not a test we will run on your submitted projects.
7 It is here for completeness.
8*/
9
10#undef NDEBUG
11#include <debug.h>
12#include <list.h>
13#include <random.h>
14#include <stdio.h>
15#include "threads/test.h"
16
17/* Maximum number of elements in a linked list that we will
18 test. */
19#define MAX_SIZE 64
20
21/* A linked list element. */
22struct value
23 {
24 struct list_elem elem; /* List element. */
25 int value; /* Item value. */
26 };
27
28static void shuffle (struct value[], size_t);
29static bool value_less (const struct list_elem *, const struct list_elem *,
30 void *);
31static void verify_list_fwd (struct list *, int size);
32static void verify_list_bkwd (struct list *, int size);
33
34/* Test the linked list implementation. */
35void
36test (void)
37{
38 int size;
39
40 printf ("testing various size lists:");
41 for (size = 0; size < MAX_SIZE; size++)
42 {
43 int repeat;
44
45 printf (" %d", size);
46 for (repeat = 0; repeat < 10; repeat++)
47 {
48 static struct value values[MAX_SIZE * 4];
49 struct list list;
50 struct list_elem *e;
51 int i, ofs;
52
53 /* Put values 0...SIZE in random order in VALUES. */
54 for (i = 0; i < size; i++)
55 values[i].value = i;
56 shuffle (values, size);
57
58 /* Assemble list. */
59 list_init (&list);
60 for (i = 0; i < size; i++)
61 list_push_back (&list, &values[i].elem);
62
63 /* Verify correct minimum and maximum elements. */
64 e = list_min (&list, value_less, NULL);
65 ASSERT (size ? list_entry (e, struct value, elem)->value == 0
66 : e == list_begin (&list));
67 e = list_max (&list, value_less, NULL);
68 ASSERT (size ? list_entry (e, struct value, elem)->value == size - 1
69 : e == list_begin (&list));
70
71 /* Sort and verify list. */
72 list_sort (&list, value_less, NULL);
73 verify_list_fwd (&list, size);
74
75 /* Reverse and verify list. */
76 list_reverse (&list);
77 verify_list_bkwd (&list, size);
78
79 /* Shuffle, insert using list_insert_ordered(),
80 and verify ordering. */
81 shuffle (values, size);
82 list_init (&list);
83 for (i = 0; i < size; i++)
84 list_insert_ordered (&list, &values[i].elem,
85 value_less, NULL);
86 verify_list_fwd (&list, size);
87
88 /* Duplicate some items, uniquify, and verify. */
89 ofs = size;
90 for (e = list_begin (&list); e != list_end (&list);
91 e = list_next (e))
92 {
93 struct value *v = list_entry (e, struct value, elem);
94 int copies = random_ulong () % 4;
95 while (copies-- > 0)
96 {
97 values[ofs].value = v->value;
98 list_insert (e, &values[ofs++].elem);
99 }
100 }
101 ASSERT ((size_t) ofs < sizeof values / sizeof *values);
102 list_unique (&list, NULL, value_less, NULL);
103 verify_list_fwd (&list, size);
104 }
105 }
106
107 printf (" done\n");
108 printf ("list: PASS\n");
109}
110
111/* Shuffles the CNT elements in ARRAY into random order. */
112static void
113shuffle (struct value *array, size_t cnt)
114{
115 size_t i;
116
117 for (i = 0; i < cnt; i++)
118 {
119 size_t j = i + random_ulong () % (cnt - i);
120 struct value t = array[j];
121 array[j] = array[i];
122 array[i] = t;
123 }
124}
125
126/* Returns true if value A is less than value B, false
127 otherwise. */
128static bool
129value_less (const struct list_elem *a_, const struct list_elem *b_,
130 void *aux UNUSED)
131{
132 const struct value *a = list_entry (a_, struct value, elem);
133 const struct value *b = list_entry (b_, struct value, elem);
134
135 return a->value < b->value;
136}
137
138/* Verifies that LIST contains the values 0...SIZE when traversed
139 in forward order. */
140static void
141verify_list_fwd (struct list *list, int size)
142{
143 struct list_elem *e;
144 int i;
145
146 for (i = 0, e = list_begin (list);
147 i < size && e != list_end (list);
148 i++, e = list_next (e))
149 {
150 struct value *v = list_entry (e, struct value, elem);
151 ASSERT (i == v->value);
152 }
153 ASSERT (i == size);
154 ASSERT (e == list_end (list));
155}
156
157/* Verifies that LIST contains the values 0...SIZE when traversed
158 in reverse order. */
159static void
160verify_list_bkwd (struct list *list, int size)
161{
162 struct list_elem *e;
163 int i;
164
165 for (i = 0, e = list_rbegin (list);
166 i < size && e != list_rend (list);
167 i++, e = list_prev (e))
168 {
169 struct value *v = list_entry (e, struct value, elem);
170 ASSERT (i == v->value);
171 }
172 ASSERT (i == size);
173 ASSERT (e == list_rend (list));
174}
diff --git a/pintos-progos/tests/internal/stdio.c b/pintos-progos/tests/internal/stdio.c
new file mode 100644
index 0000000..fb60cda
--- /dev/null
+++ b/pintos-progos/tests/internal/stdio.c
@@ -0,0 +1,208 @@
1/* Test program for printf() in lib/stdio.c.
2
3 Attempts to test printf() functionality that is not
4 sufficiently tested elsewhere in Pintos.
5
6 This is not a test we will run on your submitted projects.
7 It is here for completeness.
8*/
9
10#undef NDEBUG
11#include <limits.h>
12#include <stdarg.h>
13#include <stddef.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include <string.h>
17#include "threads/test.h"
18
19/* Number of failures so far. */
20static int failure_cnt;
21
22static void
23checkf (const char *expect, const char *format, ...)
24{
25 char output[128];
26 va_list args;
27
28 printf ("\"%s\" -> \"%s\": ", format, expect);
29
30 va_start (args, format);
31 vsnprintf (output, sizeof output, format, args);
32 va_end (args);
33
34 if (strcmp (expect, output))
35 {
36 printf ("\nFAIL: actual output \"%s\"\n", output);
37 failure_cnt++;
38 }
39 else
40 printf ("okay\n");
41}
42
43/* Test printf() implementation. */
44void
45test (void)
46{
47 printf ("Testing formats:");
48
49 /* Check that commas show up in the right places, for positive
50 numbers. */
51 checkf ("1", "%'d", 1);
52 checkf ("12", "%'d", 12);
53 checkf ("123", "%'d", 123);
54 checkf ("1,234", "%'d", 1234);
55 checkf ("12,345", "%'d", 12345);
56 checkf ("123,456", "%'ld", 123456L);
57 checkf ("1,234,567", "%'ld", 1234567L);
58 checkf ("12,345,678", "%'ld", 12345678L);
59 checkf ("123,456,789", "%'ld", 123456789L);
60 checkf ("1,234,567,890", "%'ld", 1234567890L);
61 checkf ("12,345,678,901", "%'lld", 12345678901LL);
62 checkf ("123,456,789,012", "%'lld", 123456789012LL);
63 checkf ("1,234,567,890,123", "%'lld", 1234567890123LL);
64 checkf ("12,345,678,901,234", "%'lld", 12345678901234LL);
65 checkf ("123,456,789,012,345", "%'lld", 123456789012345LL);
66 checkf ("1,234,567,890,123,456", "%'lld", 1234567890123456LL);
67 checkf ("12,345,678,901,234,567", "%'lld", 12345678901234567LL);
68 checkf ("123,456,789,012,345,678", "%'lld", 123456789012345678LL);
69 checkf ("1,234,567,890,123,456,789", "%'lld", 1234567890123456789LL);
70
71 /* Check that commas show up in the right places, for positive
72 numbers. */
73 checkf ("-1", "%'d", -1);
74 checkf ("-12", "%'d", -12);
75 checkf ("-123", "%'d", -123);
76 checkf ("-1,234", "%'d", -1234);
77 checkf ("-12,345", "%'d", -12345);
78 checkf ("-123,456", "%'ld", -123456L);
79 checkf ("-1,234,567", "%'ld", -1234567L);
80 checkf ("-12,345,678", "%'ld", -12345678L);
81 checkf ("-123,456,789", "%'ld", -123456789L);
82 checkf ("-1,234,567,890", "%'ld", -1234567890L);
83 checkf ("-12,345,678,901", "%'lld", -12345678901LL);
84 checkf ("-123,456,789,012", "%'lld", -123456789012LL);
85 checkf ("-1,234,567,890,123", "%'lld", -1234567890123LL);
86 checkf ("-12,345,678,901,234", "%'lld", -12345678901234LL);
87 checkf ("-123,456,789,012,345", "%'lld", -123456789012345LL);
88 checkf ("-1,234,567,890,123,456", "%'lld", -1234567890123456LL);
89 checkf ("-12,345,678,901,234,567", "%'lld", -12345678901234567LL);
90 checkf ("-123,456,789,012,345,678", "%'lld", -123456789012345678LL);
91 checkf ("-1,234,567,890,123,456,789", "%'lld", -1234567890123456789LL);
92
93 /* Check signed integer conversions. */
94 checkf (" 0", "%5d", 0);
95 checkf ("0 ", "%-5d", 0);
96 checkf (" +0", "%+5d", 0);
97 checkf ("+0 ", "%+-5d", 0);
98 checkf (" 0", "% 5d", 0);
99 checkf ("00000", "%05d", 0);
100 checkf (" ", "%5.0d", 0);
101 checkf (" 00", "%5.2d", 0);
102 checkf ("0", "%d", 0);
103
104 checkf (" 1", "%5d", 1);
105 checkf ("1 ", "%-5d", 1);
106 checkf (" +1", "%+5d", 1);
107 checkf ("+1 ", "%+-5d", 1);
108 checkf (" 1", "% 5d", 1);
109 checkf ("00001", "%05d", 1);
110 checkf (" 1", "%5.0d", 1);
111 checkf (" 01", "%5.2d", 1);
112 checkf ("1", "%d", 1);
113
114 checkf (" -1", "%5d", -1);
115 checkf ("-1 ", "%-5d", -1);
116 checkf (" -1", "%+5d", -1);
117 checkf ("-1 ", "%+-5d", -1);
118 checkf (" -1", "% 5d", -1);
119 checkf ("-0001", "%05d", -1);
120 checkf (" -1", "%5.0d", -1);
121 checkf (" -01", "%5.2d", -1);
122 checkf ("-1", "%d", -1);
123
124 checkf ("12345", "%5d", 12345);
125 checkf ("12345", "%-5d", 12345);
126 checkf ("+12345", "%+5d", 12345);
127 checkf ("+12345", "%+-5d", 12345);
128 checkf (" 12345", "% 5d", 12345);
129 checkf ("12345", "%05d", 12345);
130 checkf ("12345", "%5.0d", 12345);
131 checkf ("12345", "%5.2d", 12345);
132 checkf ("12345", "%d", 12345);
133
134 checkf ("123456", "%5d", 123456);
135 checkf ("123456", "%-5d", 123456);
136 checkf ("+123456", "%+5d", 123456);
137 checkf ("+123456", "%+-5d", 123456);
138 checkf (" 123456", "% 5d", 123456);
139 checkf ("123456", "%05d", 123456);
140 checkf ("123456", "%5.0d", 123456);
141 checkf ("123456", "%5.2d", 123456);
142 checkf ("123456", "%d", 123456);
143
144 /* Check unsigned integer conversions. */
145 checkf (" 0", "%5u", 0);
146 checkf (" 0", "%5o", 0);
147 checkf (" 0", "%5x", 0);
148 checkf (" 0", "%5X", 0);
149 checkf (" 0", "%#5o", 0);
150 checkf (" 0", "%#5x", 0);
151 checkf (" 0", "%#5X", 0);
152 checkf (" 00000000", "%#10.8x", 0);
153
154 checkf (" 1", "%5u", 1);
155 checkf (" 1", "%5o", 1);
156 checkf (" 1", "%5x", 1);
157 checkf (" 1", "%5X", 1);
158 checkf (" 01", "%#5o", 1);
159 checkf (" 0x1", "%#5x", 1);
160 checkf (" 0X1", "%#5X", 1);
161 checkf ("0x00000001", "%#10.8x", 1);
162
163 checkf ("123456", "%5u", 123456);
164 checkf ("361100", "%5o", 123456);
165 checkf ("1e240", "%5x", 123456);
166 checkf ("1E240", "%5X", 123456);
167 checkf ("0361100", "%#5o", 123456);
168 checkf ("0x1e240", "%#5x", 123456);
169 checkf ("0X1E240", "%#5X", 123456);
170 checkf ("0x0001e240", "%#10.8x", 123456);
171
172 /* Character and string conversions. */
173 checkf ("foobar", "%c%c%c%c%c%c", 'f', 'o', 'o', 'b', 'a', 'r');
174 checkf (" left-right ", "%6s%s%-7s", "left", "-", "right");
175 checkf ("trim", "%.4s", "trimoff");
176 checkf ("%%", "%%%%");
177
178 /* From Cristian Cadar's automatic test case generator. */
179 checkf (" abcdefgh", "%9s", "abcdefgh");
180 checkf ("36657730000", "%- o", (unsigned) 036657730000);
181 checkf ("4139757568", "%- u", (unsigned) 4139757568UL);
182 checkf ("f6bfb000", "%- x", (unsigned) 0xf6bfb000);
183 checkf ("36657730000", "%-to", (ptrdiff_t) 036657730000);
184 checkf ("4139757568", "%-tu", (ptrdiff_t) 4139757568UL);
185 checkf ("-155209728", "%-zi", (size_t) -155209728);
186 checkf ("-155209728", "%-zd", (size_t) -155209728);
187 checkf ("036657730000", "%+#o", (unsigned) 036657730000);
188 checkf ("0xf6bfb000", "%+#x", (unsigned) 0xf6bfb000);
189 checkf ("-155209728", "% zi", (size_t) -155209728);
190 checkf ("-155209728", "% zd", (size_t) -155209728);
191 checkf ("4139757568", "% tu", (ptrdiff_t) 4139757568UL);
192 checkf ("036657730000", "% #o", (unsigned) 036657730000);
193 checkf ("0xf6bfb000", "% #x", (unsigned) 0xf6bfb000);
194 checkf ("0xf6bfb000", "%# x", (unsigned) 0xf6bfb000);
195 checkf ("-155209728", "%#zd", (size_t) -155209728);
196 checkf ("-155209728", "%0zi", (size_t) -155209728);
197 checkf ("4,139,757,568", "%'tu", (ptrdiff_t) 4139757568UL);
198 checkf ("-155,209,728", "%-'d", -155209728);
199 checkf ("-155209728", "%.zi", (size_t) -155209728);
200 checkf ("-155209728", "%zi", (size_t) -155209728);
201 checkf ("-155209728", "%zd", (size_t) -155209728);
202 checkf ("-155209728", "%+zi", (size_t) -155209728);
203
204 if (failure_cnt == 0)
205 printf ("\nstdio: PASS\n");
206 else
207 printf ("\nstdio: FAIL: %d tests failed\n", failure_cnt);
208}
diff --git a/pintos-progos/tests/internal/stdlib.c b/pintos-progos/tests/internal/stdlib.c
new file mode 100644
index 0000000..ad0f0f9
--- /dev/null
+++ b/pintos-progos/tests/internal/stdlib.c
@@ -0,0 +1,114 @@
1/* Test program for sorting and searching in lib/stdlib.c.
2
3 Attempts to test the sorting and searching functionality that
4 is not sufficiently tested elsewhere in Pintos.
5
6 This is not a test we will run on your submitted projects.
7 It is here for completeness.
8*/
9
10#undef NDEBUG
11#include <debug.h>
12#include <limits.h>
13#include <random.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include "threads/test.h"
17
18/* Maximum number of elements in an array that we will test. */
19#define MAX_CNT 4096
20
21static void shuffle (int[], size_t);
22static int compare_ints (const void *, const void *);
23static void verify_order (const int[], size_t);
24static void verify_bsearch (const int[], size_t);
25
26/* Test sorting and searching implementations. */
27void
28test (void)
29{
30 int cnt;
31
32 printf ("testing various size arrays:");
33 for (cnt = 0; cnt < MAX_CNT; cnt = cnt * 4 / 3 + 1)
34 {
35 int repeat;
36
37 printf (" %zu", cnt);
38 for (repeat = 0; repeat < 10; repeat++)
39 {
40 static int values[MAX_CNT];
41 int i;
42
43 /* Put values 0...CNT in random order in VALUES. */
44 for (i = 0; i < cnt; i++)
45 values[i] = i;
46 shuffle (values, cnt);
47
48 /* Sort VALUES, then verify ordering. */
49 qsort (values, cnt, sizeof *values, compare_ints);
50 verify_order (values, cnt);
51 verify_bsearch (values, cnt);
52 }
53 }
54
55 printf (" done\n");
56 printf ("stdlib: PASS\n");
57}
58
59/* Shuffles the CNT elements in ARRAY into random order. */
60static void
61shuffle (int *array, size_t cnt)
62{
63 size_t i;
64
65 for (i = 0; i < cnt; i++)
66 {
67 size_t j = i + random_ulong () % (cnt - i);
68 int t = array[j];
69 array[j] = array[i];
70 array[i] = t;
71 }
72}
73
74/* Returns 1 if *A is greater than *B,
75 0 if *A equals *B,
76 -1 if *A is less than *B. */
77static int
78compare_ints (const void *a_, const void *b_)
79{
80 const int *a = a_;
81 const int *b = b_;
82
83 return *a < *b ? -1 : *a > *b;
84}
85
86/* Verifies that ARRAY contains the CNT ints 0...CNT-1. */
87static void
88verify_order (const int *array, size_t cnt)
89{
90 int i;
91
92 for (i = 0; (size_t) i < cnt; i++)
93 ASSERT (array[i] == i);
94}
95
96/* Checks that bsearch() works properly in ARRAY. ARRAY must
97 contain the values 0...CNT-1. */
98static void
99verify_bsearch (const int *array, size_t cnt)
100{
101 int not_in_array[] = {0, -1, INT_MAX, MAX_CNT, MAX_CNT + 1, MAX_CNT * 2};
102 int i;
103
104 /* Check that all the values in the array are found properly. */
105 for (i = 0; (size_t) i < cnt; i++)
106 ASSERT (bsearch (&i, array, cnt, sizeof *array, compare_ints)
107 == array + i);
108
109 /* Check that some values not in the array are not found. */
110 not_in_array[0] = cnt;
111 for (i = 0; (size_t) i < sizeof not_in_array / sizeof *not_in_array; i++)
112 ASSERT (bsearch (&not_in_array[i], array, cnt, sizeof *array, compare_ints)
113 == NULL);
114}
diff --git a/pintos-progos/tests/intro/Grading b/pintos-progos/tests/intro/Grading
new file mode 100644
index 0000000..b1ff6b8
--- /dev/null
+++ b/pintos-progos/tests/intro/Grading
@@ -0,0 +1,5 @@
1# Percentage of the testing point total designated for each set of
2# tests.
3
450.0% tests/intro/alarm-clock/Rubric
550.0% tests/intro/userprog-args/Rubric
diff --git a/pintos-progos/tests/intro/alarm-clock/Make.tests b/pintos-progos/tests/intro/alarm-clock/Make.tests
new file mode 100644
index 0000000..55ad443
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/Make.tests
@@ -0,0 +1,15 @@
1# -*- makefile -*-
2tests/intro/alarm-clock/%.output: SIMULATOR = bochs
3tests/intro/alarm-clock/%.output: PINTOSOPTS += --kernel-test
4tests/intro/alarm-clock/%.output: FILESYSSOURCE = --filesys-size=1
5
6# Test names.
7tests/intro/alarm-clock_TESTS = $(addprefix tests/intro/alarm-clock/,alarm-single \
8alarm-multiple alarm-simultaneous alarm-zero alarm-negative)
9
10# Sources for tests.
11tests/intro/alarm-clock_SRC = tests/intro/alarm-clock/tests.c
12tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-wait.c
13tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-simultaneous.c
14tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-zero.c
15tests/intro/alarm-clock_SRC += tests/intro/alarm-clock/alarm-negative.c
diff --git a/pintos-progos/tests/intro/alarm-clock/Rubric b/pintos-progos/tests/intro/alarm-clock/Rubric
new file mode 100644
index 0000000..0cf3dc1
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/Rubric
@@ -0,0 +1,7 @@
1Functionality and robustness of alarm clock:
24 alarm-single
34 alarm-multiple
44 alarm-simultaneous
51 alarm-zero
61 alarm-negative
7
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck b/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck
new file mode 120000
index 0000000..f3a9edc
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-multiple.ck
@@ -0,0 +1 @@
../../threads/alarm-multiple.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-negative.c b/pintos-progos/tests/intro/alarm-clock/alarm-negative.c
new file mode 120000
index 0000000..483aa63
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-negative.c
@@ -0,0 +1 @@
../../threads/alarm-negative.c \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck b/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck
new file mode 120000
index 0000000..279520e
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-negative.ck
@@ -0,0 +1 @@
../../threads/alarm-negative.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c
new file mode 120000
index 0000000..6362b61
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.c
@@ -0,0 +1 @@
../../threads/alarm-simultaneous.c \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck
new file mode 120000
index 0000000..7226d0c
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-simultaneous.ck
@@ -0,0 +1 @@
../../threads/alarm-simultaneous.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-single.ck b/pintos-progos/tests/intro/alarm-clock/alarm-single.ck
new file mode 120000
index 0000000..7f98a51
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-single.ck
@@ -0,0 +1 @@
../../threads/alarm-single.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-wait.c b/pintos-progos/tests/intro/alarm-clock/alarm-wait.c
new file mode 120000
index 0000000..2755ae5
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-wait.c
@@ -0,0 +1 @@
../../threads/alarm-wait.c \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-zero.c b/pintos-progos/tests/intro/alarm-clock/alarm-zero.c
new file mode 120000
index 0000000..a1f3ca7
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-zero.c
@@ -0,0 +1 @@
../../threads/alarm-zero.c \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck b/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck
new file mode 120000
index 0000000..3f98d64
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/alarm-zero.ck
@@ -0,0 +1 @@
../../threads/alarm-zero.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/alarm-clock/tests.c b/pintos-progos/tests/intro/alarm-clock/tests.c
new file mode 100644
index 0000000..4a96360
--- /dev/null
+++ b/pintos-progos/tests/intro/alarm-clock/tests.c
@@ -0,0 +1,80 @@
1#include "tests/threads/tests.h"
2#include <debug.h>
3#include <string.h>
4#include <stdio.h>
5
6struct test
7 {
8 const char *name;
9 test_func *function;
10 };
11
12static const struct test tests[] =
13 {
14 {"alarm-single", test_alarm_single},
15 {"alarm-multiple", test_alarm_multiple},
16 {"alarm-simultaneous", test_alarm_simultaneous},
17 {"alarm-zero", test_alarm_zero},
18 {"alarm-negative", test_alarm_negative},
19 };
20
21static const char *test_name;
22
23/* Runs the test named NAME. */
24void
25run_test (const char *name)
26{
27 const struct test *t;
28
29 for (t = tests; t < tests + sizeof tests / sizeof *tests; t++)
30 if (!strcmp (name, t->name))
31 {
32 test_name = name;
33 msg ("begin");
34 t->function ();
35 msg ("end");
36 return;
37 }
38 PANIC ("no test named \"%s\"", name);
39}
40
41/* Prints FORMAT as if with printf(),
42 prefixing the output by the name of the test
43 and following it with a new-line character. */
44void
45msg (const char *format, ...)
46{
47 va_list args;
48
49 printf ("(%s) ", test_name);
50 va_start (args, format);
51 vprintf (format, args);
52 va_end (args);
53 putchar ('\n');
54}
55
56/* Prints failure message FORMAT as if with printf(),
57 prefixing the output by the name of the test and FAIL:
58 and following it with a new-line character,
59 and then panics the kernel. */
60void
61fail (const char *format, ...)
62{
63 va_list args;
64
65 printf ("(%s) FAIL: ", test_name);
66 va_start (args, format);
67 vprintf (format, args);
68 va_end (args);
69 putchar ('\n');
70
71 PANIC ("test failed");
72}
73
74/* Prints a message indicating the current test passed. */
75void
76pass (void)
77{
78 printf ("(%s) PASS\n", test_name);
79}
80
diff --git a/pintos-progos/tests/intro/userprog-args/Make.tests b/pintos-progos/tests/intro/userprog-args/Make.tests
new file mode 100644
index 0000000..6f7a474
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/Make.tests
@@ -0,0 +1,27 @@
1# -*- makefile -*-
2
3tests/intro/userprog-args/%.output: FILESYSSOURCE = --filesys-size=2
4tests/intro/userprog-args/%.output: PUTFILES = $(filter-out kernel.bin loader.bin, $^)
5tests/intro/userprog-args/%.output: SIMULATOR = --qemu
6tests/intro/userprog-args_TESTS = $(addprefix tests/intro/userprog-args/,args-none \
7args-single args-multiple args-many args-dbl-space args-limit)
8
9tests/intro/userprog-args_PROGS = $(tests/intro/userprog-args_TESTS) $(addprefix \
10tests/intro/userprog-args/,child-simple child-args)
11
12tests/intro/userprog-args/args-none_SRC = tests/intro/userprog-args/args.c
13tests/intro/userprog-args/args-single_SRC = tests/intro/userprog-args/args.c
14tests/intro/userprog-args/args-multiple_SRC = tests/intro/userprog-args/args.c
15tests/intro/userprog-args/args-many_SRC = tests/intro/userprog-args/args.c
16tests/intro/userprog-args/args-dbl-space_SRC = tests/intro/userprog-args/args.c
17tests/intro/userprog-args/args-limit_SRC = tests/intro/userprog-args/args-limit.c
18
19tests/intro/userprog-args/child-simple_SRC = tests/intro/userprog-args/child-simple.c
20tests/intro/userprog-args/child-args_SRC = tests/intro/userprog-args/args.c
21
22$(foreach prog,$(tests/intro/userprog-args_PROGS),$(eval $(prog)_SRC += tests/lib.c))
23
24tests/intro/userprog-args/args-single_ARGS = onearg
25tests/intro/userprog-args/args-multiple_ARGS = some arguments for you!
26tests/intro/userprog-args/args-many_ARGS = a b c d e f g h i j k l m n o p q r s t u v
27tests/intro/userprog-args/args-dbl-space_ARGS = two spaces!
diff --git a/pintos-progos/tests/intro/userprog-args/Rubric b/pintos-progos/tests/intro/userprog-args/Rubric
new file mode 100644
index 0000000..f5474c5
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/Rubric
@@ -0,0 +1,7 @@
1Functionality of stack setup:
23 args-none
33 args-single
43 args-multiple
53 args-many
63 args-dbl-space
73 args-limit
diff --git a/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck b/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck
new file mode 120000
index 0000000..730d787
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-dbl-space.ck
@@ -0,0 +1 @@
../../userprog/args-dbl-space.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/args-limit.c b/pintos-progos/tests/intro/userprog-args/args-limit.c
new file mode 100644
index 0000000..159d868
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-limit.c
@@ -0,0 +1,70 @@
1/* Test the limit for (1) number of arguments and (2) total size of arguments */
2#include <syscall.h>
3#include <string.h>
4#include "tests/lib.h"
5
6#define MAX_SIZE 4096
7
8static bool recurse (int, int);
9
10char cmd[MAX_SIZE * 4];
11
12static bool
13recurse (int argsize, int argcount)
14{
15 int i, j;
16 char *p;
17 strlcpy (cmd, "args-limit", 11);
18 p = cmd+strlen(cmd);
19 for (i = 0; i < argcount; i++) {
20 *p++ = ' ';
21 for (j = 0; j < argsize; j++) {
22 *p++ = 'X';
23 }
24 }
25 *p = 0;
26 if (wait (exec (cmd)) < 0) {
27 return false;
28 } else {
29 return true;
30 }
31}
32
33int
34main (int argc, char **argv)
35{
36 test_name = argv[0];
37 if(argc <= 1) {
38 int step;
39 int max_args = 0, max_size = 0;
40
41 msg ("begin");
42
43 /* Binary search number of arguments */
44 for (step = MAX_SIZE; step > 0 && max_args < MAX_SIZE; step>>=1) {
45 int t = max_args + step;
46 if (recurse (1, t)) {
47 max_args = t;
48 }
49 }
50 if (max_args > 63) {
51 msg ("success. at least 64 command line arguments are supported.");
52 } else {
53 msg ("FAIL: Only %d command line arguments are supported",max_args);
54 }
55 /* Binary search size of arguments */
56 for (step = MAX_SIZE; step > 0 && max_size < MAX_SIZE; step>>=1) {
57 int t = max_size + step;
58 if (recurse (t, 1)) {
59 max_size = t;
60 }
61 }
62 if (max_size >= 100) {
63 msg ("success. arguments with at least 100 bytes are supported.");
64 } else {
65 msg ("FAIL: Arguments with more than %d bytes are not supported.",max_size);
66 }
67 msg ("end");
68 }
69 return 0;
70}
diff --git a/pintos-progos/tests/intro/userprog-args/args-limit.ck b/pintos-progos/tests/intro/userprog-args/args-limit.ck
new file mode 100644
index 0000000..ed8ead2
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-limit.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(args-limit) begin
7(args-limit) success. at least 64 command line arguments are supported.
8(args-limit) success. arguments with at least 100 bytes are supported.
9(args-limit) end
10EOF
11pass;
diff --git a/pintos-progos/tests/intro/userprog-args/args-many.ck b/pintos-progos/tests/intro/userprog-args/args-many.ck
new file mode 120000
index 0000000..ed175bd
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-many.ck
@@ -0,0 +1 @@
../../userprog/args-many.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/args-multiple.ck b/pintos-progos/tests/intro/userprog-args/args-multiple.ck
new file mode 120000
index 0000000..4f9935f
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-multiple.ck
@@ -0,0 +1 @@
../../userprog/args-multiple.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/args-none.ck b/pintos-progos/tests/intro/userprog-args/args-none.ck
new file mode 120000
index 0000000..861d319
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-none.ck
@@ -0,0 +1 @@
../../userprog/args-none.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/args-single.ck b/pintos-progos/tests/intro/userprog-args/args-single.ck
new file mode 120000
index 0000000..df8e737
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args-single.ck
@@ -0,0 +1 @@
../../userprog/args-single.ck \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/args.c b/pintos-progos/tests/intro/userprog-args/args.c
new file mode 120000
index 0000000..0d798ac
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/args.c
@@ -0,0 +1 @@
../../userprog/args.c \ No newline at end of file
diff --git a/pintos-progos/tests/intro/userprog-args/child-simple.c b/pintos-progos/tests/intro/userprog-args/child-simple.c
new file mode 120000
index 0000000..f8a3ce7
--- /dev/null
+++ b/pintos-progos/tests/intro/userprog-args/child-simple.c
@@ -0,0 +1 @@
../../userprog/child-simple.c \ No newline at end of file
diff --git a/pintos-progos/tests/lib.c b/pintos-progos/tests/lib.c
new file mode 100644
index 0000000..ee36505
--- /dev/null
+++ b/pintos-progos/tests/lib.c
@@ -0,0 +1,196 @@
1#include "tests/lib.h"
2#include <random.h>
3#include <stdarg.h>
4#include <stdio.h>
5#include <string.h>
6#include <syscall.h>
7
8const char *test_name;
9bool quiet = false;
10
11static void
12vmsg (const char *format, va_list args, const char *suffix)
13{
14 /* We go to some trouble to stuff the entire message into a
15 single buffer and output it in a single system call, because
16 that'll (typically) ensure that it gets sent to the console
17 atomically. Otherwise kernel messages like "foo: exit(0)"
18 can end up being interleaved if we're unlucky. */
19 static char buf[1024];
20
21 snprintf (buf, sizeof buf, "(%s) ", test_name);
22 vsnprintf (buf + strlen (buf), sizeof buf - strlen (buf), format, args);
23 strlcpy (buf + strlen (buf), suffix, sizeof buf - strlen (buf));
24 write (STDOUT_FILENO, buf, strlen (buf));
25}
26
27void
28msg (const char *format, ...)
29{
30 va_list args;
31
32 if (quiet)
33 return;
34 va_start (args, format);
35 vmsg (format, args, "\n");
36 va_end (args);
37}
38
39void
40fail (const char *format, ...)
41{
42 va_list args;
43
44 va_start (args, format);
45 vmsg (format, args, ": FAILED\n");
46 va_end (args);
47
48 exit (1);
49}
50
51static void
52swap (void *a_, void *b_, size_t size)
53{
54 uint8_t *a = a_;
55 uint8_t *b = b_;
56 size_t i;
57
58 for (i = 0; i < size; i++)
59 {
60 uint8_t t = a[i];
61 a[i] = b[i];
62 b[i] = t;
63 }
64}
65
66void
67shuffle (void *buf_, size_t cnt, size_t size)
68{
69 char *buf = buf_;
70 size_t i;
71
72 for (i = 0; i < cnt; i++)
73 {
74 size_t j = i + random_ulong () % (cnt - i);
75 swap (buf + i * size, buf + j * size, size);
76 }
77}
78
79void
80exec_children (const char *child_name, pid_t pids[], size_t child_cnt)
81{
82 size_t i;
83
84 for (i = 0; i < child_cnt; i++)
85 {
86 char cmd_line[128];
87 snprintf (cmd_line, sizeof cmd_line, "%s %zu", child_name, i);
88 CHECK ((pids[i] = exec (cmd_line)) != PID_ERROR,
89 "exec child %zu of %zu: \"%s\"", i + 1, child_cnt, cmd_line);
90 }
91}
92
93void
94wait_children (pid_t pids[], size_t child_cnt)
95{
96 size_t i;
97
98 for (i = 0; i < child_cnt; i++)
99 {
100 int status = wait (pids[i]);
101 CHECK (status == (int) i,
102 "wait for child %zu of %zu returned %d (expected %zu)",
103 i + 1, child_cnt, status, i);
104 }
105}
106
107void
108check_file_handle (int fd,
109 const char *file_name, const void *buf_, size_t size)
110{
111 const char *buf = buf_;
112 size_t ofs = 0;
113 size_t file_size;
114
115 /* Warn about file of wrong size. Don't fail yet because we
116 may still be able to get more information by reading the
117 file. */
118 file_size = filesize (fd);
119 if (file_size != size)
120 msg ("size of %s (%zu) differs from expected (%zu)",
121 file_name, file_size, size);
122
123 /* Read the file block-by-block, comparing data as we go. */
124 while (ofs < size)
125 {
126 char block[512];
127 size_t block_size, ret_val;
128
129 block_size = size - ofs;
130 if (block_size > sizeof block)
131 block_size = sizeof block;
132
133 ret_val = read (fd, block, block_size);
134 if (ret_val != block_size)
135 fail ("read of %zu bytes at offset %zu in \"%s\" returned %zu",
136 block_size, ofs, file_name, ret_val);
137
138 compare_bytes (block, buf + ofs, block_size, ofs, file_name);
139 ofs += block_size;
140 }
141
142 /* Now fail due to wrong file size. */
143 if (file_size != size)
144 fail ("size of %s (%zu) differs from expected (%zu)",
145 file_name, file_size, size);
146
147 msg ("verified contents of \"%s\"", file_name);
148}
149
150void
151check_file (const char *file_name, const void *buf, size_t size)
152{
153 int fd;
154
155 CHECK ((fd = open (file_name)) > 1, "open \"%s\" for verification",
156 file_name);
157 check_file_handle (fd, file_name, buf, size);
158 msg ("close \"%s\"", file_name);
159 close (fd);
160}
161
162void
163compare_bytes (const void *read_data_, const void *expected_data_, size_t size,
164 size_t ofs, const char *file_name)
165{
166 const uint8_t *read_data = read_data_;
167 const uint8_t *expected_data = expected_data_;
168 size_t i, j;
169 size_t show_cnt;
170
171 if (!memcmp (read_data, expected_data, size))
172 return;
173
174 for (i = 0; i < size; i++)
175 if (read_data[i] != expected_data[i])
176 break;
177 for (j = i + 1; j < size; j++)
178 if (read_data[j] == expected_data[j])
179 break;
180
181 quiet = false;
182 msg ("%zu bytes read starting at offset %zu in \"%s\" differ "
183 "from expected.", j - i, ofs + i, file_name);
184 show_cnt = j - i;
185 if (j - i > 64)
186 {
187 show_cnt = 64;
188 msg ("Showing first differing %zu bytes.", show_cnt);
189 }
190 msg ("Data actually read:");
191 hex_dump (ofs + i, read_data + i, show_cnt, true);
192 msg ("Expected data:");
193 hex_dump (ofs + i, expected_data + i, show_cnt, true);
194 fail ("%zu bytes read starting at offset %zu in \"%s\" differ "
195 "from expected", j - i, ofs + i, file_name);
196}
diff --git a/pintos-progos/tests/lib.h b/pintos-progos/tests/lib.h
new file mode 100644
index 0000000..648327b
--- /dev/null
+++ b/pintos-progos/tests/lib.h
@@ -0,0 +1,50 @@
1#ifndef TESTS_LIB_H
2#define TESTS_LIB_H
3
4#include <debug.h>
5#include <stdbool.h>
6#include <stddef.h>
7#include <syscall.h>
8
9extern const char *test_name;
10extern bool quiet;
11
12void msg (const char *, ...) PRINTF_FORMAT (1, 2);
13void fail (const char *, ...) PRINTF_FORMAT (1, 2) NO_RETURN;
14
15/* Takes an expression to test for SUCCESS and a message, which
16 may include printf-style arguments. Logs the message, then
17 tests the expression. If it is zero, indicating failure,
18 emits the message as a failure.
19
20 Somewhat tricky to use:
21
22 - SUCCESS must not have side effects that affect the
23 message, because that will cause the original message and
24 the failure message to differ.
25
26 - The message must not have side effects of its own, because
27 it will be printed twice on failure, or zero times on
28 success if quiet is set. */
29#define CHECK(SUCCESS, ...) \
30 do \
31 { \
32 msg (__VA_ARGS__); \
33 if (!(SUCCESS)) \
34 fail (__VA_ARGS__); \
35 } \
36 while (0)
37
38void shuffle (void *, size_t cnt, size_t size);
39
40void exec_children (const char *child_name, pid_t pids[], size_t child_cnt);
41void wait_children (pid_t pids[], size_t child_cnt);
42
43void check_file_handle (int fd, const char *file_name,
44 const void *buf_, size_t filesize);
45void check_file (const char *file_name, const void *buf, size_t filesize);
46
47void compare_bytes (const void *read_data, const void *expected_data,
48 size_t size, size_t ofs, const char *file_name);
49
50#endif /* test/lib.h */
diff --git a/pintos-progos/tests/lib.pm b/pintos-progos/tests/lib.pm
new file mode 100644
index 0000000..bc37ae5
--- /dev/null
+++ b/pintos-progos/tests/lib.pm
@@ -0,0 +1,19 @@
1use strict;
2use warnings;
3
4use tests::random;
5
6sub shuffle {
7 my ($in, $cnt, $sz) = @_;
8 $cnt * $sz == length $in or die;
9 my (@a) = 0...$cnt - 1;
10 for my $i (0...$cnt - 1) {
11 my ($j) = $i + random_ulong () % ($cnt - $i);
12 @a[$i, $j] = @a[$j, $i];
13 }
14 my ($out) = "";
15 $out .= substr ($in, $_ * $sz, $sz) foreach @a;
16 return $out;
17}
18
191;
diff --git a/pintos-progos/tests/main.c b/pintos-progos/tests/main.c
new file mode 100644
index 0000000..ad1b0f1
--- /dev/null
+++ b/pintos-progos/tests/main.c
@@ -0,0 +1,15 @@
1#include <random.h>
2#include "tests/lib.h"
3#include "tests/main.h"
4
5int
6main (int argc UNUSED, char *argv[])
7{
8 test_name = argv[0];
9
10 msg ("begin");
11 random_init (0);
12 test_main ();
13 msg ("end");
14 return 0;
15}
diff --git a/pintos-progos/tests/main.h b/pintos-progos/tests/main.h
new file mode 100644
index 0000000..f0e8818
--- /dev/null
+++ b/pintos-progos/tests/main.h
@@ -0,0 +1,6 @@
1#ifndef TESTS_MAIN_H
2#define TESTS_MAIN_H
3
4void test_main (void);
5
6#endif /* tests/main.h */
diff --git a/pintos-progos/tests/make-grade b/pintos-progos/tests/make-grade
new file mode 100755
index 0000000..a3faa0e
--- /dev/null
+++ b/pintos-progos/tests/make-grade
@@ -0,0 +1,152 @@
1#! /usr/bin/perl
2
3use strict;
4use warnings;
5
6@ARGV == 3 || die;
7my ($src_dir, $results_file, $grading_file) = @ARGV;
8
9# Read pass/file verdicts from $results_file.
10open (RESULTS, '<', $results_file) || die "$results_file: open: $!\n";
11my (%verdicts, %verdict_counts);
12while (<RESULTS>) {
13 my ($verdict, $test) = /^(pass|FAIL) (.*)$/ or die;
14 $verdicts{$test} = $verdict eq 'pass';
15}
16close RESULTS;
17
18my (@failures);
19my (@overall, @rubrics, @summary);
20my ($pct_actual, $pct_possible) = (0, 0);
21
22# Read grading file.
23my (@items);
24open (GRADING, '<', $grading_file) || die "$grading_file: open: $!\n";
25while (<GRADING>) {
26 s/#.*//;
27 next if /^\s*$/;
28 my ($max_pct, $rubric_suffix) = /^\s*(\d+(?:\.\d+)?)%\t(.*)/ or die;
29 my ($dir) = $rubric_suffix =~ /^(.*)\//;
30 my ($rubric_file) = "$src_dir/$rubric_suffix";
31 open (RUBRIC, '<', $rubric_file) or die "$rubric_file: open: $!\n";
32
33 # Rubric file must begin with title line.
34 my $title = <RUBRIC>;
35 chomp $title;
36 $title =~ s/:$// or die;
37 $title .= " ($rubric_suffix):";
38 push (@rubrics, $title);
39
40 my ($score, $possible) = (0, 0);
41 my ($cnt, $passed) = (0, 0);
42 my ($was_score) = 0;
43 while (<RUBRIC>) {
44 chomp;
45 push (@rubrics, "\t$_"), next if /^-/;
46 push (@rubrics, ""), next if /^\s*$/;
47 my ($poss, $name) = /^(\d+)\t(.*)$/ or die;
48 my ($test) = "$dir/$name";
49 my ($points) = 0;
50 if (!defined $verdicts{$test}) {
51 push (@overall, "warning: $test not tested, assuming failure");
52 } elsif ($verdicts{$test}) {
53 $points = $poss;
54 $passed++;
55 }
56 push (@failures, $test) if !$points;
57 $verdict_counts{$test}++;
58 push (@rubrics, sprintf ("\t%4s%2d/%2d %s",
59 $points ? '' : '**', $points, $poss, $test));
60 $score += $points;
61 $possible += $poss;
62 $cnt++;
63 }
64 close (RUBRIC);
65
66 push (@rubrics, "");
67 push (@rubrics, "\t- Section summary.");
68 push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
69 '', $passed, $cnt, 'tests passed'));
70 push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
71 '', $score, $possible, 'points subtotal'));
72 push (@rubrics, '');
73
74 my ($pct) = ($score / $possible) * $max_pct;
75 push (@summary, sprintf ("%-45s %3d/%3d %5.1f%%/%5.1f%%",
76 $rubric_suffix,
77 $score, $possible,
78 $pct, $max_pct));
79 $pct_actual += $pct;
80 $pct_possible += $max_pct;
81}
82close GRADING;
83
84my ($sum_line)
85 = "--------------------------------------------- --- --- ------ ------";
86unshift (@summary,
87 "SUMMARY BY TEST SET",
88 '',
89 sprintf ("%-45s %3s %3s %6s %6s",
90 "Test Set", "Pts", "Max", "% Ttl", "% Max"),
91 $sum_line);
92push (@summary,
93 $sum_line,
94 sprintf ("%-45s %3s %3s %5.1f%%/%5.1f%%",
95 'Total', '', '', $pct_actual, $pct_possible));
96
97unshift (@rubrics,
98 "SUMMARY OF INDIVIDUAL TESTS",
99 '');
100
101foreach my $name (keys (%verdicts)) {
102 my ($count) = $verdict_counts{$name};
103 if (!defined ($count) || $count != 1) {
104 if (!defined ($count) || !$count) {
105 push (@overall, "warning: test $name doesn't count for grading");
106 } else {
107 push (@overall,
108 "warning: test $name counted $count times in grading");
109 }
110 }
111}
112push (@overall, sprintf ("TOTAL TESTING SCORE: %.1f%%", $pct_actual));
113if (sprintf ("%.1f", $pct_actual) eq sprintf ("%.1f", $pct_possible)) {
114 push (@overall, "ALL TESTED PASSED -- PERFECT SCORE");
115}
116
117my (@divider) = ('', '- ' x 38, '');
118
119print map ("$_\n", @overall, @divider, @summary, @divider, @rubrics);
120
121for my $test (@failures) {
122 print map ("$_\n", @divider);
123 print "DETAILS OF $test FAILURE:\n\n";
124
125 if (open (RESULT, '<', "$test.result")) {
126 my $first_line = <RESULT>;
127 my ($cnt) = 0;
128 while (<RESULT>) {
129 print;
130 $cnt++;
131 }
132 close (RESULT);
133 }
134
135 if (open (OUTPUT, '<', "$test.output")) {
136 print "\nOUTPUT FROM $test:\n\n";
137
138 my ($panics, $boots) = (0, 0);
139 while (<OUTPUT>) {
140 if (/PANIC/ && ++$panics > 2) {
141 print "[...details of additional panic(s) omitted...]\n";
142 last;
143 }
144 print;
145 if (/Pintos booting/ && ++$boots > 1) {
146 print "[...details of reboot(s) omitted...]\n";
147 last;
148 }
149 }
150 close (OUTPUT);
151 }
152}
diff --git a/pintos-progos/tests/random.pm b/pintos-progos/tests/random.pm
new file mode 100644
index 0000000..be008ff
--- /dev/null
+++ b/pintos-progos/tests/random.pm
@@ -0,0 +1,27 @@
1use strict;
2use warnings;
3
4use tests::arc4;
5
6my (@arc4);
7
8sub random_init {
9 if (@arc4 == 0) {
10 my ($seed) = @_;
11 $seed = 0 if !defined $seed;
12 @arc4 = arc4_init (pack ("V", $seed));
13 }
14}
15
16sub random_bytes {
17 random_init ();
18 my ($n) = @_;
19 return arc4_crypt (\@arc4, "\0" x $n);
20}
21
22sub random_ulong {
23 random_init ();
24 return unpack ("V", random_bytes (4));
25}
26
271;
diff --git a/pintos-progos/tests/tests.pm b/pintos-progos/tests/tests.pm
new file mode 100644
index 0000000..4599cb9
--- /dev/null
+++ b/pintos-progos/tests/tests.pm
@@ -0,0 +1,625 @@
1use strict;
2use warnings;
3use tests::Algorithm::Diff;
4use File::Temp 'tempfile';
5use Fcntl qw(SEEK_SET SEEK_CUR);
6
7sub fail;
8sub pass;
9
10die if @ARGV != 2;
11our ($test, $src_dir) = @ARGV;
12
13my ($msg_file) = tempfile ();
14select ($msg_file);
15
16our (@prereq_tests) = ();
17if ($test =~ /^(.*)-persistence$/) {
18 push (@prereq_tests, $1);
19}
20for my $prereq_test (@prereq_tests) {
21 my (@result) = read_text_file ("$prereq_test.result");
22 fail "Prerequisite test $prereq_test failed.\n" if $result[0] ne 'PASS';
23}
24
25
26# Generic testing.
27
28sub check_expected {
29 my ($expected) = pop @_;
30 my (@options) = @_;
31 my (@output) = read_text_file ("$test.output");
32 common_checks ("run", @output);
33 compare_output ("run", @options, \@output, $expected);
34}
35
36sub common_checks {
37 my ($run, @output) = @_;
38
39 fail "\u$run produced no output at all\n" if @output == 0;
40
41 check_for_panic ($run, @output);
42 check_for_keyword ($run, "FAIL", @output);
43 check_for_triple_fault ($run, @output);
44 check_for_keyword ($run, "TIMEOUT", @output);
45
46 fail "\u$run didn't start up properly: no \"Pintos booting\" message\n"
47 if !grep (/Pintos booting with.*kB RAM\.\.\./, @output);
48 fail "\u$run didn't start up properly: no \"Boot complete\" message\n"
49 if !grep (/Boot complete/, @output);
50 fail "\u$run didn't shut down properly: no \"Timer: # ticks\" message\n"
51 if !grep (/Timer: \d+ ticks/, @output);
52 fail "\u$run didn't shut down properly: no \"Powering off\" message\n"
53 if !grep (/Powering off/, @output);
54}
55
56sub check_for_panic {
57 my ($run, @output) = @_;
58
59 my ($panic) = grep (/PANIC/, @output);
60 return unless defined $panic;
61
62 print "Kernel panic in $run: ", substr ($panic, index ($panic, "PANIC")),
63 "\n";
64
65 my (@stack_line) = grep (/Call stack:/, @output);
66 if (@stack_line != 0) {
67 my ($addrs) = $stack_line[0] =~ /Call stack:((?: 0x[0-9a-f]+)+)/;
68
69 # Find a user program to translate user virtual addresses.
70 my ($userprog) = "";
71 $userprog = "$test"
72 if grep (hex ($_) < 0xc0000000, split (' ', $addrs)) > 0 && -e $test;
73
74 # Get and print the backtrace.
75 my ($trace) = scalar (`backtrace kernel.o $userprog $addrs`);
76 print "Call stack:$addrs\n";
77 print "Translation of call stack:\n";
78 print $trace;
79
80 # Print disclaimer.
81 if ($userprog ne '' && index ($trace, $userprog) >= 0) {
82 print <<EOF;
83Translations of user virtual addresses above are based on a guess at
84the binary to use. If this guess is incorrect, then those
85translations will be misleading.
86EOF
87 }
88 }
89
90 if ($panic =~ /sec_no \< d-\>capacity/) {
91 print <<EOF;
92\nThis assertion commonly fails when accessing a file via an inode that
93has been closed and freed. Freeing an inode clears all its sector
94indexes to 0xcccccccc, which is not a valid sector number for disks
95smaller than about 1.6 TB.
96EOF
97 }
98
99 fail;
100}
101
102sub check_for_keyword {
103 my ($run, $keyword, @output) = @_;
104
105 my ($kw_line) = grep (/$keyword/, @output);
106 return unless defined $kw_line;
107
108 # Most output lines are prefixed by (test-name). Eliminate this
109 # from our message for brevity.
110 $kw_line =~ s/^\([^\)]+\)\s+//;
111 print "$run: $kw_line\n";
112
113 fail;
114}
115
116sub check_for_triple_fault {
117 my ($run, @output) = @_;
118
119 my ($reboots) = grep (/Pintos booting/, @output) - 1;
120 return unless $reboots > 0;
121
122 print <<EOF;
123\u$run spontaneously rebooted $reboots times.
124This is most often caused by unhandled page faults.
125Read the Triple Faults section in the Debugging chapter
126of the Pintos manual for more information.
127EOF
128
129 fail;
130}
131
132# Get @output without header or trailer.
133sub get_core_output {
134 my ($run, @output) = @_;
135 my ($p);
136
137 my ($process);
138 my ($start);
139 for my $i (0...$#_) {
140 $start = $i + 1, last
141 if ($process) = $output[$i] =~ /^Executing '(\S+).*':$/;
142 }
143
144 my ($end);
145 for my $i ($start...$#output) {
146 $end = $i - 1, last if $output[$i] =~ /^Execution of '.*' complete.$/;
147 }
148
149 fail "\u$run didn't start a thread or process\n" if !defined $start;
150 fail "\u$run started '$process' but it never finished\n" if !defined $end;
151
152 return @output[$start...$end];
153}
154
155sub compare_output {
156 my ($run) = shift @_;
157 my ($expected) = pop @_;
158 my ($output) = pop @_;
159 my (%options) = @_;
160
161 my (@output) = get_core_output ($run, @$output);
162 fail "\u$run didn't produce any output" if !@output;
163
164 my $ignore_exit_codes = exists $options{IGNORE_EXIT_CODES};
165 if ($ignore_exit_codes) {
166 delete $options{IGNORE_EXIT_CODES};
167 @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\-?\d+\)$/, @output);
168 }
169 my $ignore_user_faults = exists $options{IGNORE_USER_FAULTS};
170 if ($ignore_user_faults) {
171 delete $options{IGNORE_USER_FAULTS};
172 @output = grep (!/^Page fault at.*in user context\.$/
173 && !/: dying due to interrupt 0x0e \(.*\).$/
174 && !/^Interrupt 0x0e \(.*\) at eip=/
175 && !/^ cr2=.* error=.*/
176 && !/^ eax=.* ebx=.* ecx=.* edx=.*/
177 && !/^ esi=.* edi=.* esp=.* ebp=.*/
178 && !/^ cs=.* ds=.* es=.* ss=.*/, @output);
179 }
180 die "unknown option " . (keys (%options))[0] . "\n" if %options;
181
182 my ($msg);
183
184 # Compare actual output against each allowed output.
185 if (ref ($expected) eq 'ARRAY') {
186 my ($i) = 0;
187 $expected = {map ((++$i => $_), @$expected)};
188 }
189 foreach my $key (keys %$expected) {
190 my (@expected) = split ("\n", $expected->{$key});
191
192 $msg .= "Acceptable output:\n";
193 $msg .= join ('', map (" $_\n", @expected));
194
195 # Check whether actual and expected match.
196 # If it's a perfect match, we're done.
197 if ($#output == $#expected) {
198 my ($eq) = 1;
199 for (my ($i) = 0; $i <= $#expected; $i++) {
200 $eq = 0 if $output[$i] ne $expected[$i];
201 }
202 return $key if $eq;
203 }
204
205 # They differ. Output a diff.
206 my (@diff) = "";
207 my ($d) = Algorithm::Diff->new (\@expected, \@output);
208 while ($d->Next ()) {
209 my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2));
210 if ($d->Same ()) {
211 push (@diff, map (" $_\n", $d->Items (1)));
212 } else {
213 push (@diff, map ("- $_\n", $d->Items (1))) if $d->Items (1);
214 push (@diff, map ("+ $_\n", $d->Items (2))) if $d->Items (2);
215 }
216 }
217
218 $msg .= "Differences in `diff -u' format:\n";
219 $msg .= join ('', @diff);
220 }
221
222 # Failed to match. Report failure.
223 $msg .= "\n(Process exit codes are excluded for matching purposes.)\n"
224 if $ignore_exit_codes;
225 $msg .= "\n(User fault messages are excluded for matching purposes.)\n"
226 if $ignore_user_faults;
227 fail "Test output failed to match any acceptable form.\n\n$msg";
228}
229
230# File system extraction.
231
232# check_archive (\%CONTENTS)
233#
234# Checks that the extracted file system's contents match \%CONTENTS.
235# Each key in the hash is a file name. Each value may be:
236#
237# - $FILE: Name of a host file containing the expected contents.
238#
239# - [$FILE, $OFFSET, $LENGTH]: An excerpt of host file $FILE
240# comprising the $LENGTH bytes starting at $OFFSET.
241#
242# - [$CONTENTS]: The literal expected file contents, as a string.
243#
244# - {SUBDIR}: A subdirectory, in the same form described here,
245# recursively.
246sub check_archive {
247 my ($expected_hier) = @_;
248
249 my (@output) = read_text_file ("$test.output");
250 common_checks ("file system extraction run", @output);
251
252 @output = get_core_output ("file system extraction run", @output);
253 @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\d+\)$/, @output);
254 fail join ("\n", "Error extracting file system:", @output) if @output;
255
256 my ($test_base_name) = $test;
257 $test_base_name =~ s%.*/%%;
258 $test_base_name =~ s%-persistence$%%;
259 $expected_hier->{$test_base_name} = $prereq_tests[0];
260 $expected_hier->{'tar'} = 'tests/filesys/extended/tar';
261
262 my (%expected) = normalize_fs (flatten_hierarchy ($expected_hier, ""));
263 my (%actual) = read_tar ("$prereq_tests[0].tar");
264
265 my ($errors) = 0;
266 foreach my $name (sort keys %expected) {
267 if (exists $actual{$name}) {
268 if (is_dir ($actual{$name}) && !is_dir ($expected{$name})) {
269 print "$name is a directory but should be an ordinary file.\n";
270 $errors++;
271 } elsif (!is_dir ($actual{$name}) && is_dir ($expected{$name})) {
272 print "$name is an ordinary file but should be a directory.\n";
273 $errors++;
274 }
275 } else {
276 print "$name is missing from the file system.\n";
277 $errors++;
278 }
279 }
280 foreach my $name (sort keys %actual) {
281 if (!exists $expected{$name}) {
282 if ($name =~ /^[[:print:]]+$/) {
283 print "$name exists in the file system but it should not.\n";
284 } else {
285 my ($esc_name) = $name;
286 $esc_name =~ s/[^[:print:]]/./g;
287 print <<EOF;
288$esc_name exists in the file system but should not. (The name
289of this file contains unusual characters that were printed as `.'.)
290EOF
291 }
292 $errors++;
293 }
294 }
295 if ($errors) {
296 print "\nActual contents of file system:\n";
297 print_fs (%actual);
298 print "\nExpected contents of file system:\n";
299 print_fs (%expected);
300 } else {
301 foreach my $name (sort keys %expected) {
302 if (!is_dir ($expected{$name})) {
303 my ($exp_file, $exp_length) = open_file ($expected{$name});
304 my ($act_file, $act_length) = open_file ($actual{$name});
305 $errors += !compare_files ($exp_file, $exp_length,
306 $act_file, $act_length, $name,
307 !$errors);
308 close ($exp_file);
309 close ($act_file);
310 }
311 }
312 }
313 fail "Extracted file system contents are not correct.\n" if $errors;
314}
315
316# open_file ([$FILE, $OFFSET, $LENGTH])
317# open_file ([$CONTENTS])
318#
319# Opens a file for the contents passed in, which must be in one of
320# the two above forms that correspond to check_archive() arguments.
321#
322# Returns ($HANDLE, $LENGTH), where $HANDLE is the file's handle and
323# $LENGTH is the number of bytes in the file's content.
324sub open_file {
325 my ($value) = @_;
326 die if ref ($value) ne 'ARRAY';
327
328 my ($file) = tempfile ();
329 my ($length);
330 if (@$value == 1) {
331 $length = length ($value->[0]);
332 $file = tempfile ();
333 syswrite ($file, $value->[0]) == $length
334 or die "writing temporary file: $!\n";
335 sysseek ($file, 0, SEEK_SET);
336 } elsif (@$value == 3) {
337 $length = $value->[2];
338 open ($file, '<', $value->[0]) or die "$value->[0]: open: $!\n";
339 die "$value->[0]: file is smaller than expected\n"
340 if -s $file < $value->[1] + $length;
341 sysseek ($file, $value->[1], SEEK_SET);
342 } else {
343 die;
344 }
345 return ($file, $length);
346}
347
348# compare_files ($A, $A_SIZE, $B, $B_SIZE, $NAME, $VERBOSE)
349#
350# Compares $A_SIZE bytes in $A to $B_SIZE bytes in $B.
351# ($A and $B are handles.)
352# If their contents differ, prints a brief message describing
353# the differences, using $NAME to identify the file.
354# The message contains more detail if $VERBOSE is nonzero.
355# Returns 1 if the contents are identical, 0 otherwise.
356sub compare_files {
357 my ($a, $a_size, $b, $b_size, $name, $verbose) = @_;
358 my ($ofs) = 0;
359 select(STDOUT);
360 for (;;) {
361 my ($a_amt) = $a_size >= 1024 ? 1024 : $a_size;
362 my ($b_amt) = $b_size >= 1024 ? 1024 : $b_size;
363 my ($a_data, $b_data);
364 if (!defined (sysread ($a, $a_data, $a_amt))
365 || !defined (sysread ($b, $b_data, $b_amt))) {
366 die "reading $name: $!\n";
367 }
368
369 my ($a_len) = length $a_data;
370 my ($b_len) = length $b_data;
371 last if $a_len == 0 && $b_len == 0;
372
373 if ($a_data ne $b_data) {
374 my ($min_len) = $a_len < $b_len ? $a_len : $b_len;
375 my ($diff_ofs);
376 for ($diff_ofs = 0; $diff_ofs < $min_len; $diff_ofs++) {
377 last if (substr ($a_data, $diff_ofs, 1)
378 ne substr ($b_data, $diff_ofs, 1));
379 }
380
381 printf "\nFile $name differs from expected "
382 . "starting at offset 0x%x.\n", $ofs + $diff_ofs;
383 if ($verbose ) {
384 print "Expected contents:\n";
385 hex_dump (substr ($a_data, $diff_ofs, 64), $ofs + $diff_ofs);
386 print "Actual contents:\n";
387 hex_dump (substr ($b_data, $diff_ofs, 64), $ofs + $diff_ofs);
388 }
389 return 0;
390 }
391
392 $ofs += $a_len;
393 $a_size -= $a_len;
394 $b_size -= $b_len;
395 }
396 return 1;
397}
398
399# hex_dump ($DATA, $OFS)
400#
401# Prints $DATA in hex and text formats.
402# The first byte of $DATA corresponds to logical offset $OFS
403# in whatever file the data comes from.
404sub hex_dump {
405 my ($data, $ofs) = @_;
406
407 if ($data eq '') {
408 printf " (File ends at offset %08x.)\n", $ofs;
409 return;
410 }
411
412 my ($per_line) = 16;
413 while ((my $size = length ($data)) > 0) {
414 my ($start) = $ofs % $per_line;
415 my ($end) = $per_line;
416 $end = $start + $size if $end - $start > $size;
417 my ($n) = $end - $start;
418
419 printf "0x%08x ", int ($ofs / $per_line) * $per_line;
420
421 # Hex version.
422 print " " x $start;
423 for my $i ($start...$end - 1) {
424 printf "%02x", ord (substr ($data, $i - $start, 1));
425 print $i == $per_line / 2 - 1 ? '-' : ' ';
426 }
427 print " " x ($per_line - $end);
428
429 # Character version.
430 my ($esc_data) = substr ($data, 0, $n);
431 $esc_data =~ s/[^[:print:]]/./g;
432 print "|", " " x $start, $esc_data, " " x ($per_line - $end), "|";
433
434 print "\n";
435
436 $data = substr ($data, $n);
437 $ofs += $n;
438 }
439}
440
441# print_fs (%FS)
442#
443# Prints a list of files in %FS, which must be a file system
444# as flattened by flatten_hierarchy() and normalized by
445# normalize_fs().
446sub print_fs {
447 my (%fs) = @_;
448 foreach my $name (sort keys %fs) {
449 my ($esc_name) = $name;
450 $esc_name =~ s/[^[:print:]]/./g;
451 print "$esc_name: ";
452 if (!is_dir ($fs{$name})) {
453 print +file_size ($fs{$name}), "-byte file";
454 } else {
455 print "directory";
456 }
457 print "\n";
458 }
459 print "(empty)\n" if !@_;
460}
461
462# normalize_fs (%FS)
463#
464# Takes a file system as flattened by flatten_hierarchy().
465# Returns a similar file system in which values of the form $FILE
466# are replaced by those of the form [$FILE, $OFFSET, $LENGTH].
467sub normalize_fs {
468 my (%fs) = @_;
469 foreach my $name (keys %fs) {
470 my ($value) = $fs{$name};
471 next if is_dir ($value) || ref ($value) ne '';
472 die "can't open $value\n" if !stat $value;
473 $fs{$name} = [$value, 0, -s _];
474 }
475 return %fs;
476}
477
478# is_dir ($VALUE)
479#
480# Takes a value like one in the hash returned by flatten_hierarchy()
481# and returns 1 if it represents a directory, 0 otherwise.
482sub is_dir {
483 my ($value) = @_;
484 return ref ($value) eq '' && $value eq 'directory';
485}
486
487# file_size ($VALUE)
488#
489# Takes a value like one in the hash returned by flatten_hierarchy()
490# and returns the size of the file it represents.
491sub file_size {
492 my ($value) = @_;
493 die if is_dir ($value);
494 die if ref ($value) ne 'ARRAY';
495 return @$value > 1 ? $value->[2] : length ($value->[0]);
496}
497
498# flatten_hierarchy ($HIER_FS, $PREFIX)
499#
500# Takes a file system in the format expected by check_archive() and
501# returns a "flattened" version in which file names include all parent
502# directory names and the value of directories is just "directory".
503sub flatten_hierarchy {
504 my (%hier_fs) = %{$_[0]};
505 my ($prefix) = $_[1];
506 my (%flat_fs);
507 for my $name (keys %hier_fs) {
508 my ($value) = $hier_fs{$name};
509 if (ref $value eq 'HASH') {
510 %flat_fs = (%flat_fs, flatten_hierarchy ($value, "$prefix$name/"));
511 $flat_fs{"$prefix$name"} = 'directory';
512 } else {
513 $flat_fs{"$prefix$name"} = $value;
514 }
515 }
516 return %flat_fs;
517}
518
519# read_tar ($ARCHIVE)
520#
521# Reads the ustar-format tar file in $ARCHIVE
522# and returns a flattened file system for it.
523sub read_tar {
524 my ($archive) = @_;
525 my (%content);
526 open (ARCHIVE, '<', $archive) or fail "$archive: open: $!\n";
527 for (;;) {
528 my ($header);
529 if ((my $retval = sysread (ARCHIVE, $header, 512)) != 512) {
530 fail "$archive: unexpected end of file\n" if $retval >= 0;
531 fail "$archive: read: $!\n";
532 }
533
534 last if $header eq "\0" x 512;
535
536 # Verify magic numbers.
537 if (substr ($header, 257, 6) ne "ustar\0"
538 || substr ($header, 263, 2) ne '00') {
539 fail "$archive: corrupt ustar header\n";
540 }
541
542 # Verify checksum.
543 my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8, ' ' x 8)));
544 my ($correct_chksum) = unpack ("%32a*", $header);
545 fail "$archive: bad header checksum\n" if $chksum != $correct_chksum;
546
547 # Get file name.
548 my ($name) = unpack ("Z100", $header);
549 my ($prefix) = unpack ("Z*", substr ($header, 345));
550 $name = "$prefix/$name" if $prefix ne '';
551 fail "$archive: contains file with empty name" if $name eq '';
552
553 # Get type.
554 my ($typeflag) = substr ($header, 156, 1);
555 $typeflag = '0' if $typeflag eq "\0";
556 fail "unknown file type '$typeflag'\n" if $typeflag !~ /[05]/;
557
558 # Get size.
559 my ($size) = oct (unpack ("Z*", substr ($header, 124, 12)));
560 fail "bad size $size\n" if $size < 0;
561 $size = 0 if $typeflag eq '5';
562
563 # Store content.
564 $name =~ s%^(/|\./|\.\./)*%%; # Strip leading "/", "./", "../".
565 $name = '' if $name eq '.' || $name eq '..';
566 if (exists $content{$name}) {
567 fail "$archive: contains multiple entries for $name\n";
568 }
569 if ($typeflag eq '5') {
570 $content{$name} = 'directory' if $name ne '';
571 } else {
572 fail "$archive: contains file with empty name\n" if $name eq '';
573 my ($position) = sysseek (ARCHIVE, 0, SEEK_CUR);
574 $content{$name} = [$archive, $position, $size];
575 sysseek (ARCHIVE, int (($size + 511) / 512) * 512, SEEK_CUR);
576 }
577 }
578 close (ARCHIVE);
579 return %content;
580}
581
582# Utilities.
583
584sub fail {
585 finish ("FAIL", @_);
586}
587
588sub pass {
589 finish ("PASS", @_);
590}
591
592sub finish {
593 my ($verdict, @messages) = @_;
594
595 seek ($msg_file, 0, 0);
596 push (@messages, <$msg_file>);
597 close ($msg_file);
598 chomp (@messages);
599
600 my ($result_fn) = "$test.result";
601 open (RESULT, '>', $result_fn) or die "$result_fn: create: $!\n";
602 print RESULT "$verdict\n";
603 print RESULT "$_\n" foreach @messages;
604 close (RESULT);
605
606 if ($verdict eq 'PASS') {
607 print STDOUT "pass $test\n";
608 } else {
609 print STDOUT "FAIL $test\n";
610 }
611 print STDOUT "$_\n" foreach @messages;
612
613 exit 0;
614}
615
616sub read_text_file {
617 my ($file_name) = @_;
618 open (FILE, '<', $file_name) or die "$file_name: open: $!\n";
619 my (@content) = <FILE>;
620 chomp (@content);
621 close (FILE);
622 return @content;
623}
624
6251;
diff --git a/pintos-progos/tests/threads/Grading b/pintos-progos/tests/threads/Grading
new file mode 100644
index 0000000..cc235f2
--- /dev/null
+++ b/pintos-progos/tests/threads/Grading
@@ -0,0 +1,11 @@
1# Percentage of the testing point total designated for each set of
2# tests.
3
4# Priority Scheduling
590.0% tests/threads/Rubric.priority
6
7# Robustness
810.0% tests/threads/Rubric.alarm
9
10# Not used in SS 2012
11# XX.0% tests/threads/Rubric.mlfqs
diff --git a/pintos-progos/tests/threads/Make.tests b/pintos-progos/tests/threads/Make.tests
new file mode 100644
index 0000000..dbdfd0c
--- /dev/null
+++ b/pintos-progos/tests/threads/Make.tests
@@ -0,0 +1,56 @@
1# -*- makefile -*-
2
3# Test names.
4tests/threads_TESTS = $(addprefix tests/threads/,alarm-single \
5alarm-multiple alarm-simultaneous alarm-priority alarm-zero \
6alarm-negative priority-change priority-donate-one \
7priority-donate-multiple priority-donate-multiple2 \
8priority-donate-nest priority-donate-sema priority-donate-lower \
9priority-fifo priority-preempt priority-sema priority-condvar \
10priority-donate-chain)
11
12
13# Sources for tests.
14tests/threads_SRC = tests/threads/tests.c
15tests/threads_SRC += tests/threads/alarm-wait.c
16tests/threads_SRC += tests/threads/alarm-simultaneous.c
17tests/threads_SRC += tests/threads/alarm-priority.c
18tests/threads_SRC += tests/threads/alarm-zero.c
19tests/threads_SRC += tests/threads/alarm-negative.c
20tests/threads_SRC += tests/threads/priority-change.c
21tests/threads_SRC += tests/threads/priority-donate-one.c
22tests/threads_SRC += tests/threads/priority-donate-multiple.c
23tests/threads_SRC += tests/threads/priority-donate-multiple2.c
24tests/threads_SRC += tests/threads/priority-donate-nest.c
25tests/threads_SRC += tests/threads/priority-donate-sema.c
26tests/threads_SRC += tests/threads/priority-donate-lower.c
27tests/threads_SRC += tests/threads/priority-fifo.c
28tests/threads_SRC += tests/threads/priority-preempt.c
29tests/threads_SRC += tests/threads/priority-sema.c
30tests/threads_SRC += tests/threads/priority-condvar.c
31tests/threads_SRC += tests/threads/priority-donate-chain.c
32
33# Not used in SS 2012
34MLFQS_TESTS = mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 \
35mlfqs-fair-2 mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10 mlfqs-block)
36
37tests/threads_SRC += tests/threads/mlfqs-load-1.c
38tests/threads_SRC += tests/threads/mlfqs-load-60.c
39tests/threads_SRC += tests/threads/mlfqs-load-avg.c
40tests/threads_SRC += tests/threads/mlfqs-recent-1.c
41tests/threads_SRC += tests/threads/mlfqs-fair.c
42tests/threads_SRC += tests/threads/mlfqs-block.c
43
44MLFQS_OUTPUTS = \
45tests/threads/mlfqs-load-1.output \
46tests/threads/mlfqs-load-60.output \
47tests/threads/mlfqs-load-avg.output \
48tests/threads/mlfqs-recent-1.output \
49tests/threads/mlfqs-fair-2.output \
50tests/threads/mlfqs-fair-20.output \
51tests/threads/mlfqs-nice-2.output \
52tests/threads/mlfqs-nice-10.output \
53tests/threads/mlfqs-block.output
54
55$(MLFQS_OUTPUTS): KERNELFLAGS += -mlfqs
56$(MLFQS_OUTPUTS): TIMEOUT = 480
diff --git a/pintos-progos/tests/threads/Rubric.alarm b/pintos-progos/tests/threads/Rubric.alarm
new file mode 100644
index 0000000..61abe85
--- /dev/null
+++ b/pintos-progos/tests/threads/Rubric.alarm
@@ -0,0 +1,8 @@
1Functionality and robustness of alarm clock:
24 alarm-single
34 alarm-multiple
44 alarm-simultaneous
54 alarm-priority
6
71 alarm-zero
81 alarm-negative
diff --git a/pintos-progos/tests/threads/Rubric.mlfqs b/pintos-progos/tests/threads/Rubric.mlfqs
new file mode 100644
index 0000000..f260091
--- /dev/null
+++ b/pintos-progos/tests/threads/Rubric.mlfqs
@@ -0,0 +1,14 @@
1Functionality of advanced scheduler:
25 mlfqs-load-1
35 mlfqs-load-60
43 mlfqs-load-avg
5
65 mlfqs-recent-1
7
85 mlfqs-fair-2
93 mlfqs-fair-20
10
114 mlfqs-nice-2
122 mlfqs-nice-10
13
145 mlfqs-block
diff --git a/pintos-progos/tests/threads/Rubric.priority b/pintos-progos/tests/threads/Rubric.priority
new file mode 100644
index 0000000..652bc99
--- /dev/null
+++ b/pintos-progos/tests/threads/Rubric.priority
@@ -0,0 +1,15 @@
1Functionality of priority scheduler:
23 priority-change
33 priority-preempt
4
53 priority-fifo
63 priority-sema
73 priority-condvar
8
93 priority-donate-one
103 priority-donate-multiple
113 priority-donate-multiple2
123 priority-donate-nest
135 priority-donate-chain
143 priority-donate-sema
153 priority-donate-lower
diff --git a/pintos-progos/tests/threads/alarm-multiple.ck b/pintos-progos/tests/threads/alarm-multiple.ck
new file mode 100644
index 0000000..fd83bcd
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-multiple.ck
@@ -0,0 +1,4 @@
1# -*- perl -*-
2use tests::tests;
3use tests::threads::alarm;
4check_alarm (7);
diff --git a/pintos-progos/tests/threads/alarm-negative.c b/pintos-progos/tests/threads/alarm-negative.c
new file mode 100644
index 0000000..aec52cf
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-negative.c
@@ -0,0 +1,15 @@
1/* Tests timer_sleep(-100). Only requirement is that it not crash. */
2
3#include <stdio.h>
4#include "tests/threads/tests.h"
5#include "threads/malloc.h"
6#include "threads/synch.h"
7#include "threads/thread.h"
8#include "devices/timer.h"
9
10void
11test_alarm_negative (void)
12{
13 timer_sleep (-100);
14 pass ();
15}
diff --git a/pintos-progos/tests/threads/alarm-negative.ck b/pintos-progos/tests/threads/alarm-negative.ck
new file mode 100644
index 0000000..0d2bab0
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-negative.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(alarm-negative) begin
7(alarm-negative) PASS
8(alarm-negative) end
9EOF
10pass;
diff --git a/pintos-progos/tests/threads/alarm-priority.c b/pintos-progos/tests/threads/alarm-priority.c
new file mode 100644
index 0000000..2288ff6
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-priority.c
@@ -0,0 +1,58 @@
1/* Checks that when the alarm clock wakes up threads, the
2 higher-priority threads run first. */
3
4#include <stdio.h>
5#include "tests/threads/tests.h"
6#include "threads/init.h"
7#include "threads/malloc.h"
8#include "threads/synch.h"
9#include "threads/thread.h"
10#include "devices/timer.h"
11
12static thread_func alarm_priority_thread;
13static int64_t wake_time;
14static struct semaphore wait_sema;
15
16void
17test_alarm_priority (void)
18{
19 int i;
20
21 /* This test does not work with the MLFQS. */
22 ASSERT (!thread_mlfqs);
23
24 wake_time = timer_ticks () + 5 * TIMER_FREQ;
25 sema_init (&wait_sema, 0);
26
27 for (i = 0; i < 10; i++)
28 {
29 int priority = PRI_DEFAULT - (i + 5) % 10 - 1;
30 char name[16];
31 snprintf (name, sizeof name, "priority %d", priority);
32 thread_create (name, priority, alarm_priority_thread, NULL);
33 }
34
35 thread_set_priority (PRI_MIN);
36
37 for (i = 0; i < 10; i++)
38 sema_down (&wait_sema);
39}
40
41static void
42alarm_priority_thread (void *aux UNUSED)
43{
44 /* Busy-wait until the current time changes. */
45 int64_t start_time = timer_ticks ();
46 while (timer_elapsed (start_time) == 0)
47 continue;
48
49 /* Now we know we're at the very beginning of a timer tick, so
50 we can call timer_sleep() without worrying about races
51 between checking the time and a timer interrupt. */
52 timer_sleep (wake_time - timer_ticks ());
53
54 /* Print a message on wake-up. */
55 msg ("Thread %s woke up.", thread_name ());
56
57 sema_up (&wait_sema);
58}
diff --git a/pintos-progos/tests/threads/alarm-priority.ck b/pintos-progos/tests/threads/alarm-priority.ck
new file mode 100644
index 0000000..b57c78b
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-priority.ck
@@ -0,0 +1,19 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(alarm-priority) begin
7(alarm-priority) Thread priority 30 woke up.
8(alarm-priority) Thread priority 29 woke up.
9(alarm-priority) Thread priority 28 woke up.
10(alarm-priority) Thread priority 27 woke up.
11(alarm-priority) Thread priority 26 woke up.
12(alarm-priority) Thread priority 25 woke up.
13(alarm-priority) Thread priority 24 woke up.
14(alarm-priority) Thread priority 23 woke up.
15(alarm-priority) Thread priority 22 woke up.
16(alarm-priority) Thread priority 21 woke up.
17(alarm-priority) end
18EOF
19pass;
diff --git a/pintos-progos/tests/threads/alarm-simultaneous.c b/pintos-progos/tests/threads/alarm-simultaneous.c
new file mode 100644
index 0000000..844eea4
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-simultaneous.c
@@ -0,0 +1,94 @@
1/* Creates N threads, each of which sleeps a different, fixed
2 duration, M times. Records the wake-up order and verifies
3 that it is valid. */
4
5#include <stdio.h>
6#include "tests/threads/tests.h"
7#include "threads/init.h"
8#include "threads/malloc.h"
9#include "threads/synch.h"
10#include "threads/thread.h"
11#include "devices/timer.h"
12
13static void test_sleep (int thread_cnt, int iterations);
14
15void
16test_alarm_simultaneous (void)
17{
18 test_sleep (3, 5);
19}
20
21/* Information about the test. */
22struct sleep_test
23 {
24 int64_t start; /* Current time at start of test. */
25 int iterations; /* Number of iterations per thread. */
26 int *output_pos; /* Current position in output buffer. */
27 };
28
29static void sleeper (void *);
30
31/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
32static void
33test_sleep (int thread_cnt, int iterations)
34{
35 struct sleep_test test;
36 int *output;
37 int i;
38
39 /* This test does not work with the MLFQS. */
40 ASSERT (!thread_mlfqs);
41
42 msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
43 msg ("Each thread sleeps 10 ticks each time.");
44 msg ("Within an iteration, all threads should wake up on the same tick.");
45
46 /* Allocate memory. */
47 output = malloc (sizeof *output * iterations * thread_cnt * 2);
48 if (output == NULL)
49 PANIC ("couldn't allocate memory for test");
50
51 /* Initialize test. */
52 test.start = timer_ticks () + 100;
53 test.iterations = iterations;
54 test.output_pos = output;
55
56 /* Start threads. */
57 ASSERT (output != NULL);
58 for (i = 0; i < thread_cnt; i++)
59 {
60 char name[16];
61 snprintf (name, sizeof name, "thread %d", i);
62 thread_create (name, PRI_DEFAULT, sleeper, &test);
63 }
64
65 /* Wait long enough for all the threads to finish. */
66 timer_sleep (100 + iterations * 10 + 100);
67
68 /* Print completion order. */
69 msg ("iteration 0, thread 0: woke up after %d ticks", output[0]);
70 for (i = 1; i < test.output_pos - output; i++)
71 msg ("iteration %d, thread %d: woke up %d ticks later",
72 i / thread_cnt, i % thread_cnt, output[i] - output[i - 1]);
73
74 free (output);
75}
76
77/* Sleeper thread. */
78static void
79sleeper (void *test_)
80{
81 struct sleep_test *test = test_;
82 int i;
83
84 /* Make sure we're at the beginning of a timer tick. */
85 timer_sleep (1);
86
87 for (i = 1; i <= test->iterations; i++)
88 {
89 int64_t sleep_until = test->start + i * 10;
90 timer_sleep (sleep_until - timer_ticks ());
91 *test->output_pos++ = timer_ticks () - test->start;
92 thread_yield ();
93 }
94}
diff --git a/pintos-progos/tests/threads/alarm-simultaneous.ck b/pintos-progos/tests/threads/alarm-simultaneous.ck
new file mode 100644
index 0000000..406b8b0
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-simultaneous.ck
@@ -0,0 +1,27 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(alarm-simultaneous) begin
7(alarm-simultaneous) Creating 3 threads to sleep 5 times each.
8(alarm-simultaneous) Each thread sleeps 10 ticks each time.
9(alarm-simultaneous) Within an iteration, all threads should wake up on the same tick.
10(alarm-simultaneous) iteration 0, thread 0: woke up after 10 ticks
11(alarm-simultaneous) iteration 0, thread 1: woke up 0 ticks later
12(alarm-simultaneous) iteration 0, thread 2: woke up 0 ticks later
13(alarm-simultaneous) iteration 1, thread 0: woke up 10 ticks later
14(alarm-simultaneous) iteration 1, thread 1: woke up 0 ticks later
15(alarm-simultaneous) iteration 1, thread 2: woke up 0 ticks later
16(alarm-simultaneous) iteration 2, thread 0: woke up 10 ticks later
17(alarm-simultaneous) iteration 2, thread 1: woke up 0 ticks later
18(alarm-simultaneous) iteration 2, thread 2: woke up 0 ticks later
19(alarm-simultaneous) iteration 3, thread 0: woke up 10 ticks later
20(alarm-simultaneous) iteration 3, thread 1: woke up 0 ticks later
21(alarm-simultaneous) iteration 3, thread 2: woke up 0 ticks later
22(alarm-simultaneous) iteration 4, thread 0: woke up 10 ticks later
23(alarm-simultaneous) iteration 4, thread 1: woke up 0 ticks later
24(alarm-simultaneous) iteration 4, thread 2: woke up 0 ticks later
25(alarm-simultaneous) end
26EOF
27pass;
diff --git a/pintos-progos/tests/threads/alarm-single.ck b/pintos-progos/tests/threads/alarm-single.ck
new file mode 100644
index 0000000..31215df
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-single.ck
@@ -0,0 +1,4 @@
1# -*- perl -*-
2use tests::tests;
3use tests::threads::alarm;
4check_alarm (1);
diff --git a/pintos-progos/tests/threads/alarm-wait.c b/pintos-progos/tests/threads/alarm-wait.c
new file mode 100644
index 0000000..37d3afc
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-wait.c
@@ -0,0 +1,152 @@
1/* Creates N threads, each of which sleeps a different, fixed
2 duration, M times. Records the wake-up order and verifies
3 that it is valid. */
4
5#include <stdio.h>
6#include "tests/threads/tests.h"
7#include "threads/init.h"
8#include "threads/malloc.h"
9#include "threads/synch.h"
10#include "threads/thread.h"
11#include "devices/timer.h"
12
13static void test_sleep (int thread_cnt, int iterations);
14
15void
16test_alarm_single (void)
17{
18 test_sleep (5, 1);
19}
20
21void
22test_alarm_multiple (void)
23{
24 test_sleep (5, 7);
25}
26
27/* Information about the test. */
28struct sleep_test
29 {
30 int64_t start; /* Current time at start of test. */
31 int iterations; /* Number of iterations per thread. */
32
33 /* Output. */
34 struct lock output_lock; /* Lock protecting output buffer. */
35 int *output_pos; /* Current position in output buffer. */
36 };
37
38/* Information about an individual thread in the test. */
39struct sleep_thread
40 {
41 struct sleep_test *test; /* Info shared between all threads. */
42 int id; /* Sleeper ID. */
43 int duration; /* Number of ticks to sleep. */
44 int iterations; /* Iterations counted so far. */
45 };
46
47static void sleeper (void *);
48
49/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
50static void
51test_sleep (int thread_cnt, int iterations)
52{
53 struct sleep_test test;
54 struct sleep_thread *threads;
55 int *output, *op;
56 int product;
57 int i;
58
59 /* This test does not work with the MLFQS. */
60 ASSERT (!thread_mlfqs);
61
62 msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
63 msg ("Thread 0 sleeps 10 ticks each time,");
64 msg ("thread 1 sleeps 20 ticks each time, and so on.");
65 msg ("If successful, product of iteration count and");
66 msg ("sleep duration will appear in nondescending order.");
67
68 /* Allocate memory. */
69 threads = malloc (sizeof *threads * thread_cnt);
70 output = malloc (sizeof *output * iterations * thread_cnt * 2);
71 if (threads == NULL || output == NULL)
72 PANIC ("couldn't allocate memory for test");
73
74 /* Initialize test. */
75 test.start = timer_ticks () + 100;
76 test.iterations = iterations;
77 lock_init (&test.output_lock);
78 test.output_pos = output;
79
80 /* Start threads. */
81 ASSERT (output != NULL);
82 for (i = 0; i < thread_cnt; i++)
83 {
84 struct sleep_thread *t = threads + i;
85 char name[16];
86
87 t->test = &test;
88 t->id = i;
89 t->duration = (i + 1) * 10;
90 t->iterations = 0;
91
92 snprintf (name, sizeof name, "thread %d", i);
93 thread_create (name, PRI_DEFAULT, sleeper, t);
94 }
95
96 /* Wait long enough for all the threads to finish. */
97 timer_sleep (100 + thread_cnt * iterations * 10 + 100);
98
99 /* Acquire the output lock in case some rogue thread is still
100 running. */
101 lock_acquire (&test.output_lock);
102
103 /* Print completion order. */
104 product = 0;
105 for (op = output; op < test.output_pos; op++)
106 {
107 struct sleep_thread *t;
108 int new_prod;
109
110 ASSERT (*op >= 0 && *op < thread_cnt);
111 t = threads + *op;
112
113 new_prod = ++t->iterations * t->duration;
114
115 msg ("thread %d: duration=%d, iteration=%d, product=%d",
116 t->id, t->duration, t->iterations, new_prod);
117
118 if (new_prod >= product)
119 product = new_prod;
120 else
121 fail ("thread %d woke up out of order (%d > %d)!",
122 t->id, product, new_prod);
123 }
124
125 /* Verify that we had the proper number of wakeups. */
126 for (i = 0; i < thread_cnt; i++)
127 if (threads[i].iterations != iterations)
128 fail ("thread %d woke up %d times instead of %d",
129 i, threads[i].iterations, iterations);
130
131 lock_release (&test.output_lock);
132 free (output);
133 free (threads);
134}
135
136/* Sleeper thread. */
137static void
138sleeper (void *t_)
139{
140 struct sleep_thread *t = t_;
141 struct sleep_test *test = t->test;
142 int i;
143
144 for (i = 1; i <= test->iterations; i++)
145 {
146 int64_t sleep_until = test->start + i * t->duration;
147 timer_sleep (sleep_until - timer_ticks ());
148 lock_acquire (&test->output_lock);
149 *test->output_pos++ = t->id;
150 lock_release (&test->output_lock);
151 }
152}
diff --git a/pintos-progos/tests/threads/alarm-zero.c b/pintos-progos/tests/threads/alarm-zero.c
new file mode 100644
index 0000000..c8a3ee2
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-zero.c
@@ -0,0 +1,15 @@
1/* Tests timer_sleep(0), which should return immediately. */
2
3#include <stdio.h>
4#include "tests/threads/tests.h"
5#include "threads/malloc.h"
6#include "threads/synch.h"
7#include "threads/thread.h"
8#include "devices/timer.h"
9
10void
11test_alarm_zero (void)
12{
13 timer_sleep (0);
14 pass ();
15}
diff --git a/pintos-progos/tests/threads/alarm-zero.ck b/pintos-progos/tests/threads/alarm-zero.ck
new file mode 100644
index 0000000..a6b1a3c
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm-zero.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(alarm-zero) begin
7(alarm-zero) PASS
8(alarm-zero) end
9EOF
10pass;
diff --git a/pintos-progos/tests/threads/alarm.pm b/pintos-progos/tests/threads/alarm.pm
new file mode 100644
index 0000000..84b3b7f
--- /dev/null
+++ b/pintos-progos/tests/threads/alarm.pm
@@ -0,0 +1,32 @@
1sub check_alarm {
2 my ($iterations) = @_;
3 our ($test);
4
5 @output = read_text_file ("$test.output");
6 common_checks ("run", @output);
7
8 my (@products);
9 for (my ($i) = 0; $i < $iterations; $i++) {
10 for (my ($t) = 0; $t < 5; $t++) {
11 push (@products, ($i + 1) * ($t + 1) * 10);
12 }
13 }
14 @products = sort {$a <=> $b} @products;
15
16 local ($_);
17 foreach (@output) {
18 fail $_ if /out of order/i;
19
20 my ($p) = /product=(\d+)$/;
21 next if !defined $p;
22
23 my ($q) = shift (@products);
24 fail "Too many wakeups.\n" if !defined $q;
25 fail "Out of order wakeups ($p vs. $q).\n" if $p != $q; # FIXME
26 }
27 fail scalar (@products) . " fewer wakeups than expected.\n"
28 if @products != 0;
29 pass;
30}
31
321;
diff --git a/pintos-progos/tests/threads/mlfqs-block.c b/pintos-progos/tests/threads/mlfqs-block.c
new file mode 100644
index 0000000..6d4992d
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-block.c
@@ -0,0 +1,64 @@
1/* Checks that recent_cpu and priorities are updated for blocked
2 threads.
3
4 The main thread sleeps for 25 seconds, spins for 5 seconds,
5 then releases a lock. The "block" thread spins for 20 seconds
6 then attempts to acquire the lock, which will block for 10
7 seconds (until the main thread releases it). If recent_cpu
8 decays properly while the "block" thread sleeps, then the
9 block thread should be immediately scheduled when the main
10 thread releases the lock. */
11
12#include <stdio.h>
13#include "tests/threads/tests.h"
14#include "threads/init.h"
15#include "threads/malloc.h"
16#include "threads/synch.h"
17#include "threads/thread.h"
18#include "devices/timer.h"
19
20static void block_thread (void *lock_);
21
22void
23test_mlfqs_block (void)
24{
25 int64_t start_time;
26 struct lock lock;
27
28 ASSERT (thread_mlfqs);
29
30 msg ("Main thread acquiring lock.");
31 lock_init (&lock);
32 lock_acquire (&lock);
33
34 msg ("Main thread creating block thread, sleeping 25 seconds...");
35 thread_create ("block", PRI_DEFAULT, block_thread, &lock);
36 timer_sleep (25 * TIMER_FREQ);
37
38 msg ("Main thread spinning for 5 seconds...");
39 start_time = timer_ticks ();
40 while (timer_elapsed (start_time) < 5 * TIMER_FREQ)
41 continue;
42
43 msg ("Main thread releasing lock.");
44 lock_release (&lock);
45
46 msg ("Block thread should have already acquired lock.");
47}
48
49static void
50block_thread (void *lock_)
51{
52 struct lock *lock = lock_;
53 int64_t start_time;
54
55 msg ("Block thread spinning for 20 seconds...");
56 start_time = timer_ticks ();
57 while (timer_elapsed (start_time) < 20 * TIMER_FREQ)
58 continue;
59
60 msg ("Block thread acquiring lock...");
61 lock_acquire (lock);
62
63 msg ("...got it.");
64}
diff --git a/pintos-progos/tests/threads/mlfqs-block.ck b/pintos-progos/tests/threads/mlfqs-block.ck
new file mode 100644
index 0000000..8833a3a
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-block.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(mlfqs-block) begin
7(mlfqs-block) Main thread acquiring lock.
8(mlfqs-block) Main thread creating block thread, sleeping 25 seconds...
9(mlfqs-block) Block thread spinning for 20 seconds...
10(mlfqs-block) Block thread acquiring lock...
11(mlfqs-block) Main thread spinning for 5 seconds...
12(mlfqs-block) Main thread releasing lock.
13(mlfqs-block) ...got it.
14(mlfqs-block) Block thread should have already acquired lock.
15(mlfqs-block) end
16EOF
17pass;
diff --git a/pintos-progos/tests/threads/mlfqs-fair-2.ck b/pintos-progos/tests/threads/mlfqs-fair-2.ck
new file mode 100644
index 0000000..5b19ff1
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-fair-2.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7check_mlfqs_fair ([0, 0], 50);
diff --git a/pintos-progos/tests/threads/mlfqs-fair-20.ck b/pintos-progos/tests/threads/mlfqs-fair-20.ck
new file mode 100644
index 0000000..bb4d051
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-fair-20.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7check_mlfqs_fair ([(0) x 20], 20);
diff --git a/pintos-progos/tests/threads/mlfqs-fair.c b/pintos-progos/tests/threads/mlfqs-fair.c
new file mode 100644
index 0000000..3b1bea5
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-fair.c
@@ -0,0 +1,124 @@
1/* Measures the correctness of the "nice" implementation.
2
3 The "fair" tests run either 2 or 20 threads all niced to 0.
4 The threads should all receive approximately the same number
5 of ticks. Each test runs for 30 seconds, so the ticks should
6 also sum to approximately 30 * 100 == 3000 ticks.
7
8 The mlfqs-nice-2 test runs 2 threads, one with nice 0, the
9 other with nice 5, which should receive 1,904 and 1,096 ticks,
10 respectively, over 30 seconds.
11
12 The mlfqs-nice-10 test runs 10 threads with nice 0 through 9.
13 They should receive 672, 588, 492, 408, 316, 232, 152, 92, 40,
14 and 8 ticks, respectively, over 30 seconds.
15
16 (The above are computed via simulation in mlfqs.pm.) */
17
18#include <stdio.h>
19#include <inttypes.h>
20#include "tests/threads/tests.h"
21#include "threads/init.h"
22#include "threads/malloc.h"
23#include "threads/palloc.h"
24#include "threads/synch.h"
25#include "threads/thread.h"
26#include "devices/timer.h"
27
28static void test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step);
29
30void
31test_mlfqs_fair_2 (void)
32{
33 test_mlfqs_fair (2, 0, 0);
34}
35
36void
37test_mlfqs_fair_20 (void)
38{
39 test_mlfqs_fair (20, 0, 0);
40}
41
42void
43test_mlfqs_nice_2 (void)
44{
45 test_mlfqs_fair (2, 0, 5);
46}
47
48void
49test_mlfqs_nice_10 (void)
50{
51 test_mlfqs_fair (10, 0, 1);
52}
53
54#define MAX_THREAD_CNT 20
55
56struct thread_info
57 {
58 int64_t start_time;
59 int tick_count;
60 int nice;
61 };
62
63static void load_thread (void *aux);
64
65static void
66test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step)
67{
68 struct thread_info info[MAX_THREAD_CNT];
69 int64_t start_time;
70 int nice;
71 int i;
72
73 ASSERT (thread_mlfqs);
74 ASSERT (thread_cnt <= MAX_THREAD_CNT);
75 ASSERT (nice_min >= -10);
76 ASSERT (nice_step >= 0);
77 ASSERT (nice_min + nice_step * (thread_cnt - 1) <= 20);
78
79 thread_set_nice (-20);
80
81 start_time = timer_ticks ();
82 msg ("Starting %d threads...", thread_cnt);
83 nice = nice_min;
84 for (i = 0; i < thread_cnt; i++)
85 {
86 struct thread_info *ti = &info[i];
87 char name[16];
88
89 ti->start_time = start_time;
90 ti->tick_count = 0;
91 ti->nice = nice;
92
93 snprintf(name, sizeof name, "load %d", i);
94 thread_create (name, PRI_DEFAULT, load_thread, ti);
95
96 nice += nice_step;
97 }
98 msg ("Starting threads took %"PRId64" ticks.", timer_elapsed (start_time));
99
100 msg ("Sleeping 40 seconds to let threads run, please wait...");
101 timer_sleep (40 * TIMER_FREQ);
102
103 for (i = 0; i < thread_cnt; i++)
104 msg ("Thread %d received %d ticks.", i, info[i].tick_count);
105}
106
107static void
108load_thread (void *ti_)
109{
110 struct thread_info *ti = ti_;
111 int64_t sleep_time = 5 * TIMER_FREQ;
112 int64_t spin_time = sleep_time + 30 * TIMER_FREQ;
113 int64_t last_time = 0;
114
115 thread_set_nice (ti->nice);
116 timer_sleep (sleep_time - timer_elapsed (ti->start_time));
117 while (timer_elapsed (ti->start_time) < spin_time)
118 {
119 int64_t cur_time = timer_ticks ();
120 if (cur_time != last_time)
121 ti->tick_count++;
122 last_time = cur_time;
123 }
124}
diff --git a/pintos-progos/tests/threads/mlfqs-load-1.c b/pintos-progos/tests/threads/mlfqs-load-1.c
new file mode 100644
index 0000000..a39eea2
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-1.c
@@ -0,0 +1,60 @@
1/* Verifies that a single busy thread raises the load average to
2 0.5 in 38 to 45 seconds. The expected time is 42 seconds, as
3 you can verify:
4 perl -e '$i++,$a=(59*$a+1)/60while$a<=.5;print "$i\n"'
5
6 Then, verifies that 10 seconds of inactivity drop the load
7 average back below 0.5 again. */
8
9#include <stdio.h>
10#include "tests/threads/tests.h"
11#include "threads/init.h"
12#include "threads/malloc.h"
13#include "threads/synch.h"
14#include "threads/thread.h"
15#include "devices/timer.h"
16
17void
18test_mlfqs_load_1 (void)
19{
20 int64_t start_time;
21 int elapsed;
22 int load_avg;
23
24 ASSERT (thread_mlfqs);
25
26 msg ("spinning for up to 45 seconds, please wait...");
27
28 start_time = timer_ticks ();
29 for (;;)
30 {
31 load_avg = thread_get_load_avg ();
32 ASSERT (load_avg >= 0);
33 elapsed = timer_elapsed (start_time) / TIMER_FREQ;
34 if (load_avg > 100)
35 fail ("load average is %d.%02d "
36 "but should be between 0 and 1 (after %d seconds)",
37 load_avg / 100, load_avg % 100, elapsed);
38 else if (load_avg > 50)
39 break;
40 else if (elapsed > 45)
41 fail ("load average stayed below 0.5 for more than 45 seconds");
42 }
43
44 if (elapsed < 38)
45 fail ("load average took only %d seconds to rise above 0.5", elapsed);
46 msg ("load average rose to 0.5 after %d seconds", elapsed);
47
48 msg ("sleeping for another 10 seconds, please wait...");
49 timer_sleep (TIMER_FREQ * 10);
50
51 load_avg = thread_get_load_avg ();
52 if (load_avg < 0)
53 fail ("load average fell below 0");
54 if (load_avg > 50)
55 fail ("load average stayed above 0.5 for more than 10 seconds");
56 msg ("load average fell back below 0.5 (to %d.%02d)",
57 load_avg / 100, load_avg % 100);
58
59 pass ();
60}
diff --git a/pintos-progos/tests/threads/mlfqs-load-1.ck b/pintos-progos/tests/threads/mlfqs-load-1.ck
new file mode 100644
index 0000000..faf0ffa
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-1.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5
6our ($test);
7my (@output) = read_text_file ("$test.output");
8
9common_checks ("run", @output);
10
11@output = get_core_output ("run", @output);
12fail "missing PASS in output"
13 unless grep ($_ eq '(mlfqs-load-1) PASS', @output);
14
15pass;
diff --git a/pintos-progos/tests/threads/mlfqs-load-60.c b/pintos-progos/tests/threads/mlfqs-load-60.c
new file mode 100644
index 0000000..b6a3eb6
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-60.c
@@ -0,0 +1,155 @@
1/* Starts 60 threads that each sleep for 10 seconds, then spin in
2 a tight loop for 60 seconds, and sleep for another 60 seconds.
3 Every 2 seconds after the initial sleep, the main thread
4 prints the load average.
5
6 The expected output is this (some margin of error is allowed):
7
8 After 0 seconds, load average=1.00.
9 After 2 seconds, load average=2.95.
10 After 4 seconds, load average=4.84.
11 After 6 seconds, load average=6.66.
12 After 8 seconds, load average=8.42.
13 After 10 seconds, load average=10.13.
14 After 12 seconds, load average=11.78.
15 After 14 seconds, load average=13.37.
16 After 16 seconds, load average=14.91.
17 After 18 seconds, load average=16.40.
18 After 20 seconds, load average=17.84.
19 After 22 seconds, load average=19.24.
20 After 24 seconds, load average=20.58.
21 After 26 seconds, load average=21.89.
22 After 28 seconds, load average=23.15.
23 After 30 seconds, load average=24.37.
24 After 32 seconds, load average=25.54.
25 After 34 seconds, load average=26.68.
26 After 36 seconds, load average=27.78.
27 After 38 seconds, load average=28.85.
28 After 40 seconds, load average=29.88.
29 After 42 seconds, load average=30.87.
30 After 44 seconds, load average=31.84.
31 After 46 seconds, load average=32.77.
32 After 48 seconds, load average=33.67.
33 After 50 seconds, load average=34.54.
34 After 52 seconds, load average=35.38.
35 After 54 seconds, load average=36.19.
36 After 56 seconds, load average=36.98.
37 After 58 seconds, load average=37.74.
38 After 60 seconds, load average=37.48.
39 After 62 seconds, load average=36.24.
40 After 64 seconds, load average=35.04.
41 After 66 seconds, load average=33.88.
42 After 68 seconds, load average=32.76.
43 After 70 seconds, load average=31.68.
44 After 72 seconds, load average=30.63.
45 After 74 seconds, load average=29.62.
46 After 76 seconds, load average=28.64.
47 After 78 seconds, load average=27.69.
48 After 80 seconds, load average=26.78.
49 After 82 seconds, load average=25.89.
50 After 84 seconds, load average=25.04.
51 After 86 seconds, load average=24.21.
52 After 88 seconds, load average=23.41.
53 After 90 seconds, load average=22.64.
54 After 92 seconds, load average=21.89.
55 After 94 seconds, load average=21.16.
56 After 96 seconds, load average=20.46.
57 After 98 seconds, load average=19.79.
58 After 100 seconds, load average=19.13.
59 After 102 seconds, load average=18.50.
60 After 104 seconds, load average=17.89.
61 After 106 seconds, load average=17.30.
62 After 108 seconds, load average=16.73.
63 After 110 seconds, load average=16.17.
64 After 112 seconds, load average=15.64.
65 After 114 seconds, load average=15.12.
66 After 116 seconds, load average=14.62.
67 After 118 seconds, load average=14.14.
68 After 120 seconds, load average=13.67.
69 After 122 seconds, load average=13.22.
70 After 124 seconds, load average=12.78.
71 After 126 seconds, load average=12.36.
72 After 128 seconds, load average=11.95.
73 After 130 seconds, load average=11.56.
74 After 132 seconds, load average=11.17.
75 After 134 seconds, load average=10.80.
76 After 136 seconds, load average=10.45.
77 After 138 seconds, load average=10.10.
78 After 140 seconds, load average=9.77.
79 After 142 seconds, load average=9.45.
80 After 144 seconds, load average=9.13.
81 After 146 seconds, load average=8.83.
82 After 148 seconds, load average=8.54.
83 After 150 seconds, load average=8.26.
84 After 152 seconds, load average=7.98.
85 After 154 seconds, load average=7.72.
86 After 156 seconds, load average=7.47.
87 After 158 seconds, load average=7.22.
88 After 160 seconds, load average=6.98.
89 After 162 seconds, load average=6.75.
90 After 164 seconds, load average=6.53.
91 After 166 seconds, load average=6.31.
92 After 168 seconds, load average=6.10.
93 After 170 seconds, load average=5.90.
94 After 172 seconds, load average=5.70.
95 After 174 seconds, load average=5.52.
96 After 176 seconds, load average=5.33.
97 After 178 seconds, load average=5.16.
98*/
99
100#include <stdio.h>
101#include "tests/threads/tests.h"
102#include "threads/init.h"
103#include "threads/malloc.h"
104#include "threads/synch.h"
105#include "threads/thread.h"
106#include "devices/timer.h"
107
108static int64_t start_time;
109
110static void load_thread (void *aux);
111
112#define THREAD_CNT 60
113
114void
115test_mlfqs_load_60 (void)
116{
117 int i;
118
119 ASSERT (thread_mlfqs);
120
121 start_time = timer_ticks ();
122 msg ("Starting %d niced load threads...", THREAD_CNT);
123 for (i = 0; i < THREAD_CNT; i++)
124 {
125 char name[16];
126 snprintf(name, sizeof name, "load %d", i);
127 thread_create (name, PRI_DEFAULT, load_thread, NULL);
128 }
129 msg ("Starting threads took %d seconds.",
130 timer_elapsed (start_time) / TIMER_FREQ);
131
132 for (i = 0; i < 90; i++)
133 {
134 int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10);
135 int load_avg;
136 timer_sleep (sleep_until - timer_ticks ());
137 load_avg = thread_get_load_avg ();
138 msg ("After %d seconds, load average=%d.%02d.",
139 i * 2, load_avg / 100, load_avg % 100);
140 }
141}
142
143static void
144load_thread (void *aux UNUSED)
145{
146 int64_t sleep_time = 10 * TIMER_FREQ;
147 int64_t spin_time = sleep_time + 60 * TIMER_FREQ;
148 int64_t exit_time = spin_time + 60 * TIMER_FREQ;
149
150 thread_set_nice (20);
151 timer_sleep (sleep_time - timer_elapsed (start_time));
152 while (timer_elapsed (start_time) < spin_time)
153 continue;
154 timer_sleep (exit_time - timer_elapsed (start_time));
155}
diff --git a/pintos-progos/tests/threads/mlfqs-load-60.ck b/pintos-progos/tests/threads/mlfqs-load-60.ck
new file mode 100644
index 0000000..cb69220
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-60.ck
@@ -0,0 +1,36 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7our ($test);
8
9my (@output) = read_text_file ("$test.output");
10common_checks ("run", @output);
11@output = get_core_output ("run", @output);
12
13# Get actual values.
14local ($_);
15my (@actual);
16foreach (@output) {
17 my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./
18 or next;
19 $actual[$t] = $load_avg;
20}
21
22# Calculate expected values.
23my ($load_avg) = 0;
24my ($recent) = 0;
25my (@expected);
26for (my ($t) = 0; $t < 180; $t++) {
27 my ($ready) = $t < 60 ? 60 : 0;
28 $load_avg = (59/60) * $load_avg + (1/60) * $ready;
29 $expected[$t] = $load_avg;
30}
31
32mlfqs_compare ("time", "%.2f", \@actual, \@expected, 3.5, [2, 178, 2],
33 "Some load average values were missing or "
34 . "differed from those expected "
35 . "by more than 3.5.");
36pass;
diff --git a/pintos-progos/tests/threads/mlfqs-load-avg.c b/pintos-progos/tests/threads/mlfqs-load-avg.c
new file mode 100644
index 0000000..50e83e2
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-avg.c
@@ -0,0 +1,167 @@
1/* Starts 60 threads numbered 0 through 59. Thread #i sleeps for
2 (10+i) seconds, then spins in a loop for 60 seconds, then
3 sleeps until a total of 120 seconds have passed. Every 2
4 seconds, starting 10 seconds in, the main thread prints the
5 load average.
6
7 The expected output is listed below. Some margin of error is
8 allowed.
9
10 If your implementation fails this test but passes most other
11 tests, then consider whether you are doing too much work in
12 the timer interrupt. If the timer interrupt handler takes too
13 long, then the test's main thread will not have enough time to
14 do its own work (printing a message) and go back to sleep
15 before the next tick arrives. Then the main thread will be
16 ready, instead of sleeping, when the tick arrives,
17 artificially driving up the load average.
18
19 After 0 seconds, load average=0.00.
20 After 2 seconds, load average=0.05.
21 After 4 seconds, load average=0.16.
22 After 6 seconds, load average=0.34.
23 After 8 seconds, load average=0.58.
24 After 10 seconds, load average=0.87.
25 After 12 seconds, load average=1.22.
26 After 14 seconds, load average=1.63.
27 After 16 seconds, load average=2.09.
28 After 18 seconds, load average=2.60.
29 After 20 seconds, load average=3.16.
30 After 22 seconds, load average=3.76.
31 After 24 seconds, load average=4.42.
32 After 26 seconds, load average=5.11.
33 After 28 seconds, load average=5.85.
34 After 30 seconds, load average=6.63.
35 After 32 seconds, load average=7.46.
36 After 34 seconds, load average=8.32.
37 After 36 seconds, load average=9.22.
38 After 38 seconds, load average=10.15.
39 After 40 seconds, load average=11.12.
40 After 42 seconds, load average=12.13.
41 After 44 seconds, load average=13.16.
42 After 46 seconds, load average=14.23.
43 After 48 seconds, load average=15.33.
44 After 50 seconds, load average=16.46.
45 After 52 seconds, load average=17.62.
46 After 54 seconds, load average=18.81.
47 After 56 seconds, load average=20.02.
48 After 58 seconds, load average=21.26.
49 After 60 seconds, load average=22.52.
50 After 62 seconds, load average=23.71.
51 After 64 seconds, load average=24.80.
52 After 66 seconds, load average=25.78.
53 After 68 seconds, load average=26.66.
54 After 70 seconds, load average=27.45.
55 After 72 seconds, load average=28.14.
56 After 74 seconds, load average=28.75.
57 After 76 seconds, load average=29.27.
58 After 78 seconds, load average=29.71.
59 After 80 seconds, load average=30.06.
60 After 82 seconds, load average=30.34.
61 After 84 seconds, load average=30.55.
62 After 86 seconds, load average=30.68.
63 After 88 seconds, load average=30.74.
64 After 90 seconds, load average=30.73.
65 After 92 seconds, load average=30.66.
66 After 94 seconds, load average=30.52.
67 After 96 seconds, load average=30.32.
68 After 98 seconds, load average=30.06.
69 After 100 seconds, load average=29.74.
70 After 102 seconds, load average=29.37.
71 After 104 seconds, load average=28.95.
72 After 106 seconds, load average=28.47.
73 After 108 seconds, load average=27.94.
74 After 110 seconds, load average=27.36.
75 After 112 seconds, load average=26.74.
76 After 114 seconds, load average=26.07.
77 After 116 seconds, load average=25.36.
78 After 118 seconds, load average=24.60.
79 After 120 seconds, load average=23.81.
80 After 122 seconds, load average=23.02.
81 After 124 seconds, load average=22.26.
82 After 126 seconds, load average=21.52.
83 After 128 seconds, load average=20.81.
84 After 130 seconds, load average=20.12.
85 After 132 seconds, load average=19.46.
86 After 134 seconds, load average=18.81.
87 After 136 seconds, load average=18.19.
88 After 138 seconds, load average=17.59.
89 After 140 seconds, load average=17.01.
90 After 142 seconds, load average=16.45.
91 After 144 seconds, load average=15.90.
92 After 146 seconds, load average=15.38.
93 After 148 seconds, load average=14.87.
94 After 150 seconds, load average=14.38.
95 After 152 seconds, load average=13.90.
96 After 154 seconds, load average=13.44.
97 After 156 seconds, load average=13.00.
98 After 158 seconds, load average=12.57.
99 After 160 seconds, load average=12.15.
100 After 162 seconds, load average=11.75.
101 After 164 seconds, load average=11.36.
102 After 166 seconds, load average=10.99.
103 After 168 seconds, load average=10.62.
104 After 170 seconds, load average=10.27.
105 After 172 seconds, load average=9.93.
106 After 174 seconds, load average=9.61.
107 After 176 seconds, load average=9.29.
108 After 178 seconds, load average=8.98.
109*/
110
111#include <stdio.h>
112#include "tests/threads/tests.h"
113#include "threads/init.h"
114#include "threads/malloc.h"
115#include "threads/synch.h"
116#include "threads/thread.h"
117#include "devices/timer.h"
118
119static int64_t start_time;
120
121static void load_thread (void *seq_no);
122
123#define THREAD_CNT 60
124
125void
126test_mlfqs_load_avg (void)
127{
128 int i;
129
130 ASSERT (thread_mlfqs);
131
132 start_time = timer_ticks ();
133 msg ("Starting %d load threads...", THREAD_CNT);
134 for (i = 0; i < THREAD_CNT; i++)
135 {
136 char name[16];
137 snprintf(name, sizeof name, "load %d", i);
138 thread_create (name, PRI_DEFAULT, load_thread, (void *) i);
139 }
140 msg ("Starting threads took %d seconds.",
141 timer_elapsed (start_time) / TIMER_FREQ);
142 thread_set_nice (-20);
143
144 for (i = 0; i < 90; i++)
145 {
146 int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10);
147 int load_avg;
148 timer_sleep (sleep_until - timer_ticks ());
149 load_avg = thread_get_load_avg ();
150 msg ("After %d seconds, load average=%d.%02d.",
151 i * 2, load_avg / 100, load_avg % 100);
152 }
153}
154
155static void
156load_thread (void *seq_no_)
157{
158 int seq_no = (int) seq_no_;
159 int sleep_time = TIMER_FREQ * (10 + seq_no);
160 int spin_time = sleep_time + TIMER_FREQ * THREAD_CNT;
161 int exit_time = TIMER_FREQ * (THREAD_CNT * 2);
162
163 timer_sleep (sleep_time - timer_elapsed (start_time));
164 while (timer_elapsed (start_time) < spin_time)
165 continue;
166 timer_sleep (exit_time - timer_elapsed (start_time));
167}
diff --git a/pintos-progos/tests/threads/mlfqs-load-avg.ck b/pintos-progos/tests/threads/mlfqs-load-avg.ck
new file mode 100644
index 0000000..2254d05
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-load-avg.ck
@@ -0,0 +1,36 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7our ($test);
8my (@output) = read_text_file ("$test.output");
9
10common_checks ("run", @output);
11@output = get_core_output ("run", @output);
12
13# Get actual values.
14local ($_);
15my (@actual);
16foreach (@output) {
17 my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./
18 or next;
19 $actual[$t] = $load_avg;
20}
21
22# Calculate expected values.
23my ($load_avg) = 0;
24my ($recent) = 0;
25my (@expected);
26for (my ($t) = 0; $t < 180; $t++) {
27 my ($ready) = $t < 60 ? $t : $t < 120 ? 120 - $t : 0;
28 $load_avg = (59/60) * $load_avg + (1/60) * $ready;
29 $expected[$t] = $load_avg;
30}
31
32mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2],
33 "Some load average values were missing or "
34 . "differed from those expected "
35 . "by more than 2.5.");
36pass;
diff --git a/pintos-progos/tests/threads/mlfqs-nice-10.ck b/pintos-progos/tests/threads/mlfqs-nice-10.ck
new file mode 100644
index 0000000..53e0abe
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-nice-10.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7check_mlfqs_fair ([0...9], 25);
diff --git a/pintos-progos/tests/threads/mlfqs-nice-2.ck b/pintos-progos/tests/threads/mlfqs-nice-2.ck
new file mode 100644
index 0000000..ada366b
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-nice-2.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7check_mlfqs_fair ([0, 5], 50);
diff --git a/pintos-progos/tests/threads/mlfqs-recent-1.c b/pintos-progos/tests/threads/mlfqs-recent-1.c
new file mode 100644
index 0000000..4258671
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-recent-1.c
@@ -0,0 +1,144 @@
1/* Checks that recent_cpu is calculated properly for the case of
2 a single ready process.
3
4 The expected output is this (some margin of error is allowed):
5
6 After 2 seconds, recent_cpu is 6.40, load_avg is 0.03.
7 After 4 seconds, recent_cpu is 12.60, load_avg is 0.07.
8 After 6 seconds, recent_cpu is 18.61, load_avg is 0.10.
9 After 8 seconds, recent_cpu is 24.44, load_avg is 0.13.
10 After 10 seconds, recent_cpu is 30.08, load_avg is 0.15.
11 After 12 seconds, recent_cpu is 35.54, load_avg is 0.18.
12 After 14 seconds, recent_cpu is 40.83, load_avg is 0.21.
13 After 16 seconds, recent_cpu is 45.96, load_avg is 0.24.
14 After 18 seconds, recent_cpu is 50.92, load_avg is 0.26.
15 After 20 seconds, recent_cpu is 55.73, load_avg is 0.29.
16 After 22 seconds, recent_cpu is 60.39, load_avg is 0.31.
17 After 24 seconds, recent_cpu is 64.90, load_avg is 0.33.
18 After 26 seconds, recent_cpu is 69.27, load_avg is 0.35.
19 After 28 seconds, recent_cpu is 73.50, load_avg is 0.38.
20 After 30 seconds, recent_cpu is 77.60, load_avg is 0.40.
21 After 32 seconds, recent_cpu is 81.56, load_avg is 0.42.
22 After 34 seconds, recent_cpu is 85.40, load_avg is 0.44.
23 After 36 seconds, recent_cpu is 89.12, load_avg is 0.45.
24 After 38 seconds, recent_cpu is 92.72, load_avg is 0.47.
25 After 40 seconds, recent_cpu is 96.20, load_avg is 0.49.
26 After 42 seconds, recent_cpu is 99.57, load_avg is 0.51.
27 After 44 seconds, recent_cpu is 102.84, load_avg is 0.52.
28 After 46 seconds, recent_cpu is 106.00, load_avg is 0.54.
29 After 48 seconds, recent_cpu is 109.06, load_avg is 0.55.
30 After 50 seconds, recent_cpu is 112.02, load_avg is 0.57.
31 After 52 seconds, recent_cpu is 114.89, load_avg is 0.58.
32 After 54 seconds, recent_cpu is 117.66, load_avg is 0.60.
33 After 56 seconds, recent_cpu is 120.34, load_avg is 0.61.
34 After 58 seconds, recent_cpu is 122.94, load_avg is 0.62.
35 After 60 seconds, recent_cpu is 125.46, load_avg is 0.64.
36 After 62 seconds, recent_cpu is 127.89, load_avg is 0.65.
37 After 64 seconds, recent_cpu is 130.25, load_avg is 0.66.
38 After 66 seconds, recent_cpu is 132.53, load_avg is 0.67.
39 After 68 seconds, recent_cpu is 134.73, load_avg is 0.68.
40 After 70 seconds, recent_cpu is 136.86, load_avg is 0.69.
41 After 72 seconds, recent_cpu is 138.93, load_avg is 0.70.
42 After 74 seconds, recent_cpu is 140.93, load_avg is 0.71.
43 After 76 seconds, recent_cpu is 142.86, load_avg is 0.72.
44 After 78 seconds, recent_cpu is 144.73, load_avg is 0.73.
45 After 80 seconds, recent_cpu is 146.54, load_avg is 0.74.
46 After 82 seconds, recent_cpu is 148.29, load_avg is 0.75.
47 After 84 seconds, recent_cpu is 149.99, load_avg is 0.76.
48 After 86 seconds, recent_cpu is 151.63, load_avg is 0.76.
49 After 88 seconds, recent_cpu is 153.21, load_avg is 0.77.
50 After 90 seconds, recent_cpu is 154.75, load_avg is 0.78.
51 After 92 seconds, recent_cpu is 156.23, load_avg is 0.79.
52 After 94 seconds, recent_cpu is 157.67, load_avg is 0.79.
53 After 96 seconds, recent_cpu is 159.06, load_avg is 0.80.
54 After 98 seconds, recent_cpu is 160.40, load_avg is 0.81.
55 After 100 seconds, recent_cpu is 161.70, load_avg is 0.81.
56 After 102 seconds, recent_cpu is 162.96, load_avg is 0.82.
57 After 104 seconds, recent_cpu is 164.18, load_avg is 0.83.
58 After 106 seconds, recent_cpu is 165.35, load_avg is 0.83.
59 After 108 seconds, recent_cpu is 166.49, load_avg is 0.84.
60 After 110 seconds, recent_cpu is 167.59, load_avg is 0.84.
61 After 112 seconds, recent_cpu is 168.66, load_avg is 0.85.
62 After 114 seconds, recent_cpu is 169.69, load_avg is 0.85.
63 After 116 seconds, recent_cpu is 170.69, load_avg is 0.86.
64 After 118 seconds, recent_cpu is 171.65, load_avg is 0.86.
65 After 120 seconds, recent_cpu is 172.58, load_avg is 0.87.
66 After 122 seconds, recent_cpu is 173.49, load_avg is 0.87.
67 After 124 seconds, recent_cpu is 174.36, load_avg is 0.88.
68 After 126 seconds, recent_cpu is 175.20, load_avg is 0.88.
69 After 128 seconds, recent_cpu is 176.02, load_avg is 0.88.
70 After 130 seconds, recent_cpu is 176.81, load_avg is 0.89.
71 After 132 seconds, recent_cpu is 177.57, load_avg is 0.89.
72 After 134 seconds, recent_cpu is 178.31, load_avg is 0.89.
73 After 136 seconds, recent_cpu is 179.02, load_avg is 0.90.
74 After 138 seconds, recent_cpu is 179.72, load_avg is 0.90.
75 After 140 seconds, recent_cpu is 180.38, load_avg is 0.90.
76 After 142 seconds, recent_cpu is 181.03, load_avg is 0.91.
77 After 144 seconds, recent_cpu is 181.65, load_avg is 0.91.
78 After 146 seconds, recent_cpu is 182.26, load_avg is 0.91.
79 After 148 seconds, recent_cpu is 182.84, load_avg is 0.92.
80 After 150 seconds, recent_cpu is 183.41, load_avg is 0.92.
81 After 152 seconds, recent_cpu is 183.96, load_avg is 0.92.
82 After 154 seconds, recent_cpu is 184.49, load_avg is 0.92.
83 After 156 seconds, recent_cpu is 185.00, load_avg is 0.93.
84 After 158 seconds, recent_cpu is 185.49, load_avg is 0.93.
85 After 160 seconds, recent_cpu is 185.97, load_avg is 0.93.
86 After 162 seconds, recent_cpu is 186.43, load_avg is 0.93.
87 After 164 seconds, recent_cpu is 186.88, load_avg is 0.94.
88 After 166 seconds, recent_cpu is 187.31, load_avg is 0.94.
89 After 168 seconds, recent_cpu is 187.73, load_avg is 0.94.
90 After 170 seconds, recent_cpu is 188.14, load_avg is 0.94.
91 After 172 seconds, recent_cpu is 188.53, load_avg is 0.94.
92 After 174 seconds, recent_cpu is 188.91, load_avg is 0.95.
93 After 176 seconds, recent_cpu is 189.27, load_avg is 0.95.
94 After 178 seconds, recent_cpu is 189.63, load_avg is 0.95.
95 After 180 seconds, recent_cpu is 189.97, load_avg is 0.95.
96*/
97
98#include <stdio.h>
99#include "tests/threads/tests.h"
100#include "threads/init.h"
101#include "threads/malloc.h"
102#include "threads/synch.h"
103#include "threads/thread.h"
104#include "devices/timer.h"
105
106/* Sensitive to assumption that recent_cpu updates happen exactly
107 when timer_ticks() % TIMER_FREQ == 0. */
108
109void
110test_mlfqs_recent_1 (void)
111{
112 int64_t start_time;
113 int last_elapsed = 0;
114
115 ASSERT (thread_mlfqs);
116
117 do
118 {
119 msg ("Sleeping 10 seconds to allow recent_cpu to decay, please wait...");
120 start_time = timer_ticks ();
121 timer_sleep (DIV_ROUND_UP (start_time, TIMER_FREQ) - start_time
122 + 10 * TIMER_FREQ);
123 }
124 while (thread_get_recent_cpu () > 700);
125
126 start_time = timer_ticks ();
127 for (;;)
128 {
129 int elapsed = timer_elapsed (start_time);
130 if (elapsed % (TIMER_FREQ * 2) == 0 && elapsed > last_elapsed)
131 {
132 int recent_cpu = thread_get_recent_cpu ();
133 int load_avg = thread_get_load_avg ();
134 int elapsed_seconds = elapsed / TIMER_FREQ;
135 msg ("After %d seconds, recent_cpu is %d.%02d, load_avg is %d.%02d.",
136 elapsed_seconds,
137 recent_cpu / 100, recent_cpu % 100,
138 load_avg / 100, load_avg % 100);
139 if (elapsed_seconds >= 180)
140 break;
141 }
142 last_elapsed = elapsed;
143 }
144}
diff --git a/pintos-progos/tests/threads/mlfqs-recent-1.ck b/pintos-progos/tests/threads/mlfqs-recent-1.ck
new file mode 100644
index 0000000..a2ba44d
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs-recent-1.ck
@@ -0,0 +1,31 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::threads::mlfqs;
6
7our ($test);
8my (@output) = read_text_file ("$test.output");
9common_checks ("run", @output);
10@output = get_core_output ("run", @output);
11
12# Get actual values.
13local ($_);
14my (@actual);
15foreach (@output) {
16 my ($t, $recent_cpu) = /After (\d+) seconds, recent_cpu is (\d+\.\d+),/
17 or next;
18 $actual[$t] = $recent_cpu;
19}
20
21# Calculate expected values.
22my ($expected_load_avg, $expected_recent_cpu)
23 = mlfqs_expected_load ([(1) x 180], [(100) x 180]);
24my (@expected) = @$expected_recent_cpu;
25
26# Compare actual and expected values.
27mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2],
28 "Some recent_cpu values were missing or "
29 . "differed from those expected "
30 . "by more than 2.5.");
31pass;
diff --git a/pintos-progos/tests/threads/mlfqs.pm b/pintos-progos/tests/threads/mlfqs.pm
new file mode 100644
index 0000000..184ac16
--- /dev/null
+++ b/pintos-progos/tests/threads/mlfqs.pm
@@ -0,0 +1,146 @@
1# -*- perl -*-
2use strict;
3use warnings;
4
5sub mlfqs_expected_load {
6 my ($ready, $recent_delta) = @_;
7 my (@load_avg) = 0;
8 my (@recent_cpu) = 0;
9 my ($load_avg) = 0;
10 my ($recent_cpu) = 0;
11 for my $i (0...$#$ready) {
12 $load_avg = (59/60) * $load_avg + (1/60) * $ready->[$i];
13 push (@load_avg, $load_avg);
14
15 if (defined $recent_delta->[$i]) {
16 my ($twice_load) = $load_avg * 2;
17 my ($load_factor) = $twice_load / ($twice_load + 1);
18 $recent_cpu = ($recent_cpu + $recent_delta->[$i]) * $load_factor;
19 push (@recent_cpu, $recent_cpu);
20 }
21 }
22 return (\@load_avg, \@recent_cpu);
23}
24
25sub mlfqs_expected_ticks {
26 my (@nice) = @_;
27 my ($thread_cnt) = scalar (@nice);
28 my (@recent_cpu) = (0) x $thread_cnt;
29 my (@slices) = (0) x $thread_cnt;
30 my (@fifo) = (0) x $thread_cnt;
31 my ($next_fifo) = 1;
32 my ($load_avg) = 0;
33 for my $i (1...750) {
34 if ($i % 25 == 0) {
35 # Update load average.
36 $load_avg = (59/60) * $load_avg + (1/60) * $thread_cnt;
37
38 # Update recent_cpu.
39 my ($twice_load) = $load_avg * 2;
40 my ($load_factor) = $twice_load / ($twice_load + 1);
41 $recent_cpu[$_] = $recent_cpu[$_] * $load_factor + $nice[$_]
42 foreach 0...($thread_cnt - 1);
43 }
44
45 # Update priorities.
46 my (@priority);
47 foreach my $j (0...($thread_cnt - 1)) {
48 my ($priority) = int ($recent_cpu[$j] / 4 + $nice[$j] * 2);
49 $priority = 0 if $priority < 0;
50 $priority = 63 if $priority > 63;
51 push (@priority, $priority);
52 }
53
54 # Choose thread to run.
55 my $max = 0;
56 for my $j (1...$#priority) {
57 if ($priority[$j] < $priority[$max]
58 || ($priority[$j] == $priority[$max]
59 && $fifo[$j] < $fifo[$max])) {
60 $max = $j;
61 }
62 }
63 $fifo[$max] = $next_fifo++;
64
65 # Run thread.
66 $recent_cpu[$max] += 4;
67 $slices[$max] += 4;
68 }
69 return @slices;
70}
71
72sub check_mlfqs_fair {
73 my ($nice, $maxdiff) = @_;
74 our ($test);
75 my (@output) = read_text_file ("$test.output");
76 common_checks ("run", @output);
77 @output = get_core_output ("run", @output);
78
79 my (@actual);
80 local ($_);
81 foreach (@output) {
82 my ($id, $count) = /Thread (\d+) received (\d+) ticks\./ or next;
83 $actual[$id] = $count;
84 }
85
86 my (@expected) = mlfqs_expected_ticks (@$nice);
87 mlfqs_compare ("thread", "%d",
88 \@actual, \@expected, $maxdiff, [0, $#$nice, 1],
89 "Some tick counts were missing or differed from those "
90 . "expected by more than $maxdiff.");
91 pass;
92}
93
94sub mlfqs_compare {
95 my ($indep_var, $format,
96 $actual_ref, $expected_ref, $maxdiff, $t_range, $message) = @_;
97 my ($t_min, $t_max, $t_step) = @$t_range;
98
99 my ($ok) = 1;
100 for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) {
101 my ($actual) = $actual_ref->[$t];
102 my ($expected) = $expected_ref->[$t];
103 $ok = 0, last
104 if !defined ($actual) || abs ($actual - $expected) > $maxdiff + .01;
105 }
106 return if $ok;
107
108 print "$message\n";
109 mlfqs_row ($indep_var, "actual", "<->", "expected", "explanation");
110 mlfqs_row ("------", "--------", "---", "--------", '-' x 40);
111 for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) {
112 my ($actual) = $actual_ref->[$t];
113 my ($expected) = $expected_ref->[$t];
114 my ($diff, $rationale);
115 if (!defined $actual) {
116 $actual = 'undef' ;
117 $diff = '';
118 $rationale = 'Missing value.';
119 } else {
120 my ($delta) = abs ($actual - $expected);
121 if ($delta > $maxdiff + .01) {
122 my ($excess) = $delta - $maxdiff;
123 if ($actual > $expected) {
124 $diff = '>>>';
125 $rationale = sprintf "Too big, by $format.", $excess;
126 } else {
127 $diff = '<<<';
128 $rationale = sprintf "Too small, by $format.", $excess;
129 }
130 } else {
131 $diff = ' = ';
132 $rationale = '';
133 }
134 $actual = sprintf ($format, $actual);
135 }
136 $expected = sprintf ($format, $expected);
137 mlfqs_row ($t, $actual, $diff, $expected, $rationale);
138 }
139 fail;
140}
141
142sub mlfqs_row {
143 printf "%6s %8s %3s %-8s %s\n", @_;
144}
145
1461;
diff --git a/pintos-progos/tests/threads/priority-change.c b/pintos-progos/tests/threads/priority-change.c
new file mode 100644
index 0000000..810b05a
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-change.c
@@ -0,0 +1,31 @@
1/* Verifies that lowering a thread's priority so that it is no
2 longer the highest-priority thread in the system causes it to
3 yield immediately. */
4
5#include <stdio.h>
6#include "tests/threads/tests.h"
7#include "threads/init.h"
8#include "threads/thread.h"
9
10static thread_func changing_thread;
11
12void
13test_priority_change (void)
14{
15 /* This test does not work with the MLFQS. */
16 ASSERT (!thread_mlfqs);
17
18 msg ("Creating a high-priority thread 2.");
19 thread_create ("thread 2", PRI_DEFAULT + 1, changing_thread, NULL);
20 msg ("Thread 2 should have just lowered its priority.");
21 thread_set_priority (PRI_DEFAULT - 2);
22 msg ("Thread 2 should have just exited.");
23}
24
25static void
26changing_thread (void *aux UNUSED)
27{
28 msg ("Thread 2 now lowering priority.");
29 thread_set_priority (PRI_DEFAULT - 1);
30 msg ("Thread 2 exiting.");
31}
diff --git a/pintos-progos/tests/threads/priority-change.ck b/pintos-progos/tests/threads/priority-change.ck
new file mode 100644
index 0000000..f4d9b2f
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-change.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-change) begin
7(priority-change) Creating a high-priority thread 2.
8(priority-change) Thread 2 now lowering priority.
9(priority-change) Thread 2 should have just lowered its priority.
10(priority-change) Thread 2 exiting.
11(priority-change) Thread 2 should have just exited.
12(priority-change) end
13EOF
14pass;
diff --git a/pintos-progos/tests/threads/priority-condvar.c b/pintos-progos/tests/threads/priority-condvar.c
new file mode 100644
index 0000000..c1efb1b
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-condvar.c
@@ -0,0 +1,53 @@
1/* Tests that cond_signal() wakes up the highest-priority thread
2 waiting in cond_wait(). */
3
4#include <stdio.h>
5#include "tests/threads/tests.h"
6#include "threads/init.h"
7#include "threads/malloc.h"
8#include "threads/synch.h"
9#include "threads/thread.h"
10#include "devices/timer.h"
11
12static thread_func priority_condvar_thread;
13static struct lock lock;
14static struct condition condition;
15
16void
17test_priority_condvar (void)
18{
19 int i;
20
21 /* This test does not work with the MLFQS. */
22 ASSERT (!thread_mlfqs);
23
24 lock_init (&lock);
25 cond_init (&condition);
26
27 thread_set_priority (PRI_MIN);
28 for (i = 0; i < 10; i++)
29 {
30 int priority = PRI_DEFAULT - (i + 7) % 10 - 1;
31 char name[16];
32 snprintf (name, sizeof name, "priority %d", priority);
33 thread_create (name, priority, priority_condvar_thread, NULL);
34 }
35
36 for (i = 0; i < 10; i++)
37 {
38 lock_acquire (&lock);
39 msg ("Signaling...");
40 cond_signal (&condition, &lock);
41 lock_release (&lock);
42 }
43}
44
45static void
46priority_condvar_thread (void *aux UNUSED)
47{
48 msg ("Thread %s starting.", thread_name ());
49 lock_acquire (&lock);
50 cond_wait (&condition, &lock);
51 msg ("Thread %s woke up.", thread_name ());
52 lock_release (&lock);
53}
diff --git a/pintos-progos/tests/threads/priority-condvar.ck b/pintos-progos/tests/threads/priority-condvar.ck
new file mode 100644
index 0000000..195c1ab
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-condvar.ck
@@ -0,0 +1,39 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-condvar) begin
7(priority-condvar) Thread priority 23 starting.
8(priority-condvar) Thread priority 22 starting.
9(priority-condvar) Thread priority 21 starting.
10(priority-condvar) Thread priority 30 starting.
11(priority-condvar) Thread priority 29 starting.
12(priority-condvar) Thread priority 28 starting.
13(priority-condvar) Thread priority 27 starting.
14(priority-condvar) Thread priority 26 starting.
15(priority-condvar) Thread priority 25 starting.
16(priority-condvar) Thread priority 24 starting.
17(priority-condvar) Signaling...
18(priority-condvar) Thread priority 30 woke up.
19(priority-condvar) Signaling...
20(priority-condvar) Thread priority 29 woke up.
21(priority-condvar) Signaling...
22(priority-condvar) Thread priority 28 woke up.
23(priority-condvar) Signaling...
24(priority-condvar) Thread priority 27 woke up.
25(priority-condvar) Signaling...
26(priority-condvar) Thread priority 26 woke up.
27(priority-condvar) Signaling...
28(priority-condvar) Thread priority 25 woke up.
29(priority-condvar) Signaling...
30(priority-condvar) Thread priority 24 woke up.
31(priority-condvar) Signaling...
32(priority-condvar) Thread priority 23 woke up.
33(priority-condvar) Signaling...
34(priority-condvar) Thread priority 22 woke up.
35(priority-condvar) Signaling...
36(priority-condvar) Thread priority 21 woke up.
37(priority-condvar) end
38EOF
39pass;
diff --git a/pintos-progos/tests/threads/priority-donate-chain.c b/pintos-progos/tests/threads/priority-donate-chain.c
new file mode 100644
index 0000000..3ffabca
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-chain.c
@@ -0,0 +1,114 @@
1/* The main thread set its priority to PRI_MIN and creates 7 threads
2 (thread 1..7) with priorities PRI_MIN + 3, 6, 9, 12, ...
3 The main thread initializes 8 locks: lock 0..7 and acquires lock 0.
4
5 When thread[i] starts, it first acquires lock[i] (unless i == 7.)
6 Subsequently, thread[i] attempts to acquire lock[i-1], which is held by
7 thread[i-1], except for lock[0], which is held by the main thread.
8 Because the lock is held, thread[i] donates its priority to thread[i-1],
9 which donates to thread[i-2], and so on until the main thread
10 receives the donation.
11
12 After threads[1..7] have been created and are blocked on locks[0..7],
13 the main thread releases lock[0], unblocking thread[1], and being
14 preempted by it.
15 Thread[1] then completes acquiring lock[0], then releases lock[0],
16 then releases lock[1], unblocking thread[2], etc.
17 Thread[7] finally acquires & releases lock[7] and exits, allowing
18 thread[6], then thread[5] etc. to run and exit until finally the
19 main thread exits.
20
21 In addition, interloper threads are created at priority levels
22 p = PRI_MIN + 2, 5, 8, 11, ... which should not be run until the
23 corresponding thread with priority p + 1 has finished.
24
25 Written by Godmar Back <gback@cs.vt.edu> */
26
27#include <stdio.h>
28#include "tests/threads/tests.h"
29#include "threads/init.h"
30#include "threads/synch.h"
31#include "threads/thread.h"
32
33#define NESTING_DEPTH 8
34
35struct lock_pair
36 {
37 struct lock *second;
38 struct lock *first;
39 };
40
41static thread_func donor_thread_func;
42static thread_func interloper_thread_func;
43
44void
45test_priority_donate_chain (void)
46{
47 int i;
48 struct lock locks[NESTING_DEPTH - 1];
49 struct lock_pair lock_pairs[NESTING_DEPTH];
50
51 /* This test does not work with the MLFQS. */
52 ASSERT (!thread_mlfqs);
53
54 thread_set_priority (PRI_MIN);
55
56 for (i = 0; i < NESTING_DEPTH - 1; i++)
57 lock_init (&locks[i]);
58
59 lock_acquire (&locks[0]);
60 msg ("%s got lock.", thread_name ());
61
62 for (i = 1; i < NESTING_DEPTH; i++)
63 {
64 char name[16];
65 int thread_priority;
66
67 snprintf (name, sizeof name, "thread %d", i);
68 thread_priority = PRI_MIN + i * 3;
69 lock_pairs[i].first = i < NESTING_DEPTH - 1 ? locks + i: NULL;
70 lock_pairs[i].second = locks + i - 1;
71
72 thread_create (name, thread_priority, donor_thread_func, lock_pairs + i);
73 msg ("%s should have priority %d. Actual priority: %d.",
74 thread_name (), thread_priority, thread_get_priority ());
75
76 snprintf (name, sizeof name, "interloper %d", i);
77 thread_create (name, thread_priority - 1, interloper_thread_func, NULL);
78 }
79
80 lock_release (&locks[0]);
81 msg ("%s finishing with priority %d.", thread_name (),
82 thread_get_priority ());
83}
84
85static void
86donor_thread_func (void *locks_)
87{
88 struct lock_pair *locks = locks_;
89
90 if (locks->first)
91 lock_acquire (locks->first);
92
93 lock_acquire (locks->second);
94 msg ("%s got lock", thread_name ());
95
96 lock_release (locks->second);
97 msg ("%s should have priority %d. Actual priority: %d",
98 thread_name (), (NESTING_DEPTH - 1) * 3,
99 thread_get_priority ());
100
101 if (locks->first)
102 lock_release (locks->first);
103
104 msg ("%s finishing with priority %d.", thread_name (),
105 thread_get_priority ());
106}
107
108static void
109interloper_thread_func (void *arg_ UNUSED)
110{
111 msg ("%s finished.", thread_name ());
112}
113
114// vim: sw=2
diff --git a/pintos-progos/tests/threads/priority-donate-chain.ck b/pintos-progos/tests/threads/priority-donate-chain.ck
new file mode 100644
index 0000000..213e649
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-chain.ck
@@ -0,0 +1,46 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-chain) begin
7(priority-donate-chain) main got lock.
8(priority-donate-chain) main should have priority 3. Actual priority: 3.
9(priority-donate-chain) main should have priority 6. Actual priority: 6.
10(priority-donate-chain) main should have priority 9. Actual priority: 9.
11(priority-donate-chain) main should have priority 12. Actual priority: 12.
12(priority-donate-chain) main should have priority 15. Actual priority: 15.
13(priority-donate-chain) main should have priority 18. Actual priority: 18.
14(priority-donate-chain) main should have priority 21. Actual priority: 21.
15(priority-donate-chain) thread 1 got lock
16(priority-donate-chain) thread 1 should have priority 21. Actual priority: 21
17(priority-donate-chain) thread 2 got lock
18(priority-donate-chain) thread 2 should have priority 21. Actual priority: 21
19(priority-donate-chain) thread 3 got lock
20(priority-donate-chain) thread 3 should have priority 21. Actual priority: 21
21(priority-donate-chain) thread 4 got lock
22(priority-donate-chain) thread 4 should have priority 21. Actual priority: 21
23(priority-donate-chain) thread 5 got lock
24(priority-donate-chain) thread 5 should have priority 21. Actual priority: 21
25(priority-donate-chain) thread 6 got lock
26(priority-donate-chain) thread 6 should have priority 21. Actual priority: 21
27(priority-donate-chain) thread 7 got lock
28(priority-donate-chain) thread 7 should have priority 21. Actual priority: 21
29(priority-donate-chain) thread 7 finishing with priority 21.
30(priority-donate-chain) interloper 7 finished.
31(priority-donate-chain) thread 6 finishing with priority 18.
32(priority-donate-chain) interloper 6 finished.
33(priority-donate-chain) thread 5 finishing with priority 15.
34(priority-donate-chain) interloper 5 finished.
35(priority-donate-chain) thread 4 finishing with priority 12.
36(priority-donate-chain) interloper 4 finished.
37(priority-donate-chain) thread 3 finishing with priority 9.
38(priority-donate-chain) interloper 3 finished.
39(priority-donate-chain) thread 2 finishing with priority 6.
40(priority-donate-chain) interloper 2 finished.
41(priority-donate-chain) thread 1 finishing with priority 3.
42(priority-donate-chain) interloper 1 finished.
43(priority-donate-chain) main finishing with priority 0.
44(priority-donate-chain) end
45EOF
46pass;
diff --git a/pintos-progos/tests/threads/priority-donate-lower.c b/pintos-progos/tests/threads/priority-donate-lower.c
new file mode 100644
index 0000000..4965d75
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-lower.c
@@ -0,0 +1,51 @@
1/* The main thread acquires a lock. Then it creates a
2 higher-priority thread that blocks acquiring the lock, causing
3 it to donate their priorities to the main thread. The main
4 thread attempts to lower its priority, which should not take
5 effect until the donation is released. */
6
7#include <stdio.h>
8#include "tests/threads/tests.h"
9#include "threads/init.h"
10#include "threads/synch.h"
11#include "threads/thread.h"
12
13static thread_func acquire_thread_func;
14
15void
16test_priority_donate_lower (void)
17{
18 struct lock lock;
19
20 /* This test does not work with the MLFQS. */
21 ASSERT (!thread_mlfqs);
22
23 /* Make sure our priority is the default. */
24 ASSERT (thread_get_priority () == PRI_DEFAULT);
25
26 lock_init (&lock);
27 lock_acquire (&lock);
28 thread_create ("acquire", PRI_DEFAULT + 10, acquire_thread_func, &lock);
29 msg ("Main thread should have priority %d. Actual priority: %d.",
30 PRI_DEFAULT + 10, thread_get_priority ());
31
32 msg ("Lowering base priority...");
33 thread_set_priority (PRI_DEFAULT - 10);
34 msg ("Main thread should have priority %d. Actual priority: %d.",
35 PRI_DEFAULT + 10, thread_get_priority ());
36 lock_release (&lock);
37 msg ("acquire must already have finished.");
38 msg ("Main thread should have priority %d. Actual priority: %d.",
39 PRI_DEFAULT - 10, thread_get_priority ());
40}
41
42static void
43acquire_thread_func (void *lock_)
44{
45 struct lock *lock = lock_;
46
47 lock_acquire (lock);
48 msg ("acquire: got the lock");
49 lock_release (lock);
50 msg ("acquire: done");
51}
diff --git a/pintos-progos/tests/threads/priority-donate-lower.ck b/pintos-progos/tests/threads/priority-donate-lower.ck
new file mode 100644
index 0000000..c9bb61b
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-lower.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-lower) begin
7(priority-donate-lower) Main thread should have priority 41. Actual priority: 41.
8(priority-donate-lower) Lowering base priority...
9(priority-donate-lower) Main thread should have priority 41. Actual priority: 41.
10(priority-donate-lower) acquire: got the lock
11(priority-donate-lower) acquire: done
12(priority-donate-lower) acquire must already have finished.
13(priority-donate-lower) Main thread should have priority 21. Actual priority: 21.
14(priority-donate-lower) end
15EOF
16pass;
diff --git a/pintos-progos/tests/threads/priority-donate-multiple.c b/pintos-progos/tests/threads/priority-donate-multiple.c
new file mode 100644
index 0000000..df4689c
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-multiple.c
@@ -0,0 +1,77 @@
1/* The main thread acquires locks A and B, then it creates two
2 higher-priority threads. Each of these threads blocks
3 acquiring one of the locks and thus donate their priority to
4 the main thread. The main thread releases the locks in turn
5 and relinquishes its donated priorities.
6
7 Based on a test originally submitted for Stanford's CS 140 in
8 winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
9 Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
10 <yph@cs.stanford.edu>. Modified by arens. */
11
12#include <stdio.h>
13#include "tests/threads/tests.h"
14#include "threads/init.h"
15#include "threads/synch.h"
16#include "threads/thread.h"
17
18static thread_func a_thread_func;
19static thread_func b_thread_func;
20
21void
22test_priority_donate_multiple (void)
23{
24 struct lock a, b;
25
26 /* This test does not work with the MLFQS. */
27 ASSERT (!thread_mlfqs);
28
29 /* Make sure our priority is the default. */
30 ASSERT (thread_get_priority () == PRI_DEFAULT);
31
32 lock_init (&a);
33 lock_init (&b);
34
35 lock_acquire (&a);
36 lock_acquire (&b);
37
38 thread_create ("a", PRI_DEFAULT + 1, a_thread_func, &a);
39 msg ("Main thread should have priority %d. Actual priority: %d.",
40 PRI_DEFAULT + 1, thread_get_priority ());
41
42 thread_create ("b", PRI_DEFAULT + 2, b_thread_func, &b);
43 msg ("Main thread should have priority %d. Actual priority: %d.",
44 PRI_DEFAULT + 2, thread_get_priority ());
45
46 lock_release (&b);
47 msg ("Thread b should have just finished.");
48 msg ("Main thread should have priority %d. Actual priority: %d.",
49 PRI_DEFAULT + 1, thread_get_priority ());
50
51 lock_release (&a);
52 msg ("Thread a should have just finished.");
53 msg ("Main thread should have priority %d. Actual priority: %d.",
54 PRI_DEFAULT, thread_get_priority ());
55}
56
57static void
58a_thread_func (void *lock_)
59{
60 struct lock *lock = lock_;
61
62 lock_acquire (lock);
63 msg ("Thread a acquired lock a.");
64 lock_release (lock);
65 msg ("Thread a finished.");
66}
67
68static void
69b_thread_func (void *lock_)
70{
71 struct lock *lock = lock_;
72
73 lock_acquire (lock);
74 msg ("Thread b acquired lock b.");
75 lock_release (lock);
76 msg ("Thread b finished.");
77}
diff --git a/pintos-progos/tests/threads/priority-donate-multiple.ck b/pintos-progos/tests/threads/priority-donate-multiple.ck
new file mode 100644
index 0000000..0afd20b
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-multiple.ck
@@ -0,0 +1,19 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-multiple) begin
7(priority-donate-multiple) Main thread should have priority 32. Actual priority: 32.
8(priority-donate-multiple) Main thread should have priority 33. Actual priority: 33.
9(priority-donate-multiple) Thread b acquired lock b.
10(priority-donate-multiple) Thread b finished.
11(priority-donate-multiple) Thread b should have just finished.
12(priority-donate-multiple) Main thread should have priority 32. Actual priority: 32.
13(priority-donate-multiple) Thread a acquired lock a.
14(priority-donate-multiple) Thread a finished.
15(priority-donate-multiple) Thread a should have just finished.
16(priority-donate-multiple) Main thread should have priority 31. Actual priority: 31.
17(priority-donate-multiple) end
18EOF
19pass;
diff --git a/pintos-progos/tests/threads/priority-donate-multiple2.c b/pintos-progos/tests/threads/priority-donate-multiple2.c
new file mode 100644
index 0000000..7f65fef
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-multiple2.c
@@ -0,0 +1,90 @@
1/* The main thread acquires locks A and B, then it creates three
2 higher-priority threads. The first two of these threads block
3 acquiring one of the locks and thus donate their priority to
4 the main thread. The main thread releases the locks in turn
5 and relinquishes its donated priorities, allowing the third thread
6 to run.
7
8 In this test, the main thread releases the locks in a different
9 order compared to priority-donate-multiple.c.
10
11 Written by Godmar Back <gback@cs.vt.edu>.
12 Based on a test originally submitted for Stanford's CS 140 in
13 winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
14 Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
15 <yph@cs.stanford.edu>. Modified by arens. */
16
17#include <stdio.h>
18#include "tests/threads/tests.h"
19#include "threads/init.h"
20#include "threads/synch.h"
21#include "threads/thread.h"
22
23static thread_func a_thread_func;
24static thread_func b_thread_func;
25static thread_func c_thread_func;
26
27void
28test_priority_donate_multiple2 (void)
29{
30 struct lock a, b;
31
32 /* This test does not work with the MLFQS. */
33 ASSERT (!thread_mlfqs);
34
35 /* Make sure our priority is the default. */
36 ASSERT (thread_get_priority () == PRI_DEFAULT);
37
38 lock_init (&a);
39 lock_init (&b);
40
41 lock_acquire (&a);
42 lock_acquire (&b);
43
44 thread_create ("a", PRI_DEFAULT + 3, a_thread_func, &a);
45 msg ("Main thread should have priority %d. Actual priority: %d.",
46 PRI_DEFAULT + 3, thread_get_priority ());
47
48 thread_create ("c", PRI_DEFAULT + 1, c_thread_func, NULL);
49
50 thread_create ("b", PRI_DEFAULT + 5, b_thread_func, &b);
51 msg ("Main thread should have priority %d. Actual priority: %d.",
52 PRI_DEFAULT + 5, thread_get_priority ());
53
54 lock_release (&a);
55 msg ("Main thread should have priority %d. Actual priority: %d.",
56 PRI_DEFAULT + 5, thread_get_priority ());
57
58 lock_release (&b);
59 msg ("Threads b, a, c should have just finished, in that order.");
60 msg ("Main thread should have priority %d. Actual priority: %d.",
61 PRI_DEFAULT, thread_get_priority ());
62}
63
64static void
65a_thread_func (void *lock_)
66{
67 struct lock *lock = lock_;
68
69 lock_acquire (lock);
70 msg ("Thread a acquired lock a.");
71 lock_release (lock);
72 msg ("Thread a finished.");
73}
74
75static void
76b_thread_func (void *lock_)
77{
78 struct lock *lock = lock_;
79
80 lock_acquire (lock);
81 msg ("Thread b acquired lock b.");
82 lock_release (lock);
83 msg ("Thread b finished.");
84}
85
86static void
87c_thread_func (void *a_ UNUSED)
88{
89 msg ("Thread c finished.");
90}
diff --git a/pintos-progos/tests/threads/priority-donate-multiple2.ck b/pintos-progos/tests/threads/priority-donate-multiple2.ck
new file mode 100644
index 0000000..b23533a
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-multiple2.ck
@@ -0,0 +1,19 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-multiple2) begin
7(priority-donate-multiple2) Main thread should have priority 34. Actual priority: 34.
8(priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36.
9(priority-donate-multiple2) Main thread should have priority 36. Actual priority: 36.
10(priority-donate-multiple2) Thread b acquired lock b.
11(priority-donate-multiple2) Thread b finished.
12(priority-donate-multiple2) Thread a acquired lock a.
13(priority-donate-multiple2) Thread a finished.
14(priority-donate-multiple2) Thread c finished.
15(priority-donate-multiple2) Threads b, a, c should have just finished, in that order.
16(priority-donate-multiple2) Main thread should have priority 31. Actual priority: 31.
17(priority-donate-multiple2) end
18EOF
19pass;
diff --git a/pintos-progos/tests/threads/priority-donate-nest.c b/pintos-progos/tests/threads/priority-donate-nest.c
new file mode 100644
index 0000000..3a3a9a5
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-nest.c
@@ -0,0 +1,94 @@
1/* Low-priority main thread L acquires lock A. Medium-priority
2 thread M then acquires lock B then blocks on acquiring lock A.
3 High-priority thread H then blocks on acquiring lock B. Thus,
4 thread H donates its priority to M, which in turn donates it
5 to thread L.
6
7 Based on a test originally submitted for Stanford's CS 140 in
8 winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
9 Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
10 <yph@cs.stanford.edu>. Modified by arens. */
11
12#include <stdio.h>
13#include "tests/threads/tests.h"
14#include "threads/init.h"
15#include "threads/synch.h"
16#include "threads/thread.h"
17
18struct locks
19 {
20 struct lock *a;
21 struct lock *b;
22 };
23
24static thread_func medium_thread_func;
25static thread_func high_thread_func;
26
27void
28test_priority_donate_nest (void)
29{
30 struct lock a, b;
31 struct locks locks;
32
33 /* This test does not work with the MLFQS. */
34 ASSERT (!thread_mlfqs);
35
36 /* Make sure our priority is the default. */
37 ASSERT (thread_get_priority () == PRI_DEFAULT);
38
39 lock_init (&a);
40 lock_init (&b);
41
42 lock_acquire (&a);
43
44 locks.a = &a;
45 locks.b = &b;
46 thread_create ("medium", PRI_DEFAULT + 1, medium_thread_func, &locks);
47 thread_yield ();
48 msg ("Low thread should have priority %d. Actual priority: %d.",
49 PRI_DEFAULT + 1, thread_get_priority ());
50
51 thread_create ("high", PRI_DEFAULT + 2, high_thread_func, &b);
52 thread_yield ();
53 msg ("Low thread should have priority %d. Actual priority: %d.",
54 PRI_DEFAULT + 2, thread_get_priority ());
55
56 lock_release (&a);
57 thread_yield ();
58 msg ("Medium thread should just have finished.");
59 msg ("Low thread should have priority %d. Actual priority: %d.",
60 PRI_DEFAULT, thread_get_priority ());
61}
62
63static void
64medium_thread_func (void *locks_)
65{
66 struct locks *locks = locks_;
67
68 lock_acquire (locks->b);
69 lock_acquire (locks->a);
70
71 msg ("Medium thread should have priority %d. Actual priority: %d.",
72 PRI_DEFAULT + 2, thread_get_priority ());
73 msg ("Medium thread got the lock.");
74
75 lock_release (locks->a);
76 thread_yield ();
77
78 lock_release (locks->b);
79 thread_yield ();
80
81 msg ("High thread should have just finished.");
82 msg ("Middle thread finished.");
83}
84
85static void
86high_thread_func (void *lock_)
87{
88 struct lock *lock = lock_;
89
90 lock_acquire (lock);
91 msg ("High thread got the lock.");
92 lock_release (lock);
93 msg ("High thread finished.");
94}
diff --git a/pintos-progos/tests/threads/priority-donate-nest.ck b/pintos-progos/tests/threads/priority-donate-nest.ck
new file mode 100644
index 0000000..923460e
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-nest.ck
@@ -0,0 +1,19 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-nest) begin
7(priority-donate-nest) Low thread should have priority 32. Actual priority: 32.
8(priority-donate-nest) Low thread should have priority 33. Actual priority: 33.
9(priority-donate-nest) Medium thread should have priority 33. Actual priority: 33.
10(priority-donate-nest) Medium thread got the lock.
11(priority-donate-nest) High thread got the lock.
12(priority-donate-nest) High thread finished.
13(priority-donate-nest) High thread should have just finished.
14(priority-donate-nest) Middle thread finished.
15(priority-donate-nest) Medium thread should just have finished.
16(priority-donate-nest) Low thread should have priority 31. Actual priority: 31.
17(priority-donate-nest) end
18EOF
19pass;
diff --git a/pintos-progos/tests/threads/priority-donate-one.c b/pintos-progos/tests/threads/priority-donate-one.c
new file mode 100644
index 0000000..3189f3a
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-one.c
@@ -0,0 +1,65 @@
1/* The main thread acquires a lock. Then it creates two
2 higher-priority threads that block acquiring the lock, causing
3 them to donate their priorities to the main thread. When the
4 main thread releases the lock, the other threads should
5 acquire it in priority order.
6
7 Based on a test originally submitted for Stanford's CS 140 in
8 winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
9 Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
10 <yph@cs.stanford.edu>. Modified by arens. */
11
12#include <stdio.h>
13#include "tests/threads/tests.h"
14#include "threads/init.h"
15#include "threads/synch.h"
16#include "threads/thread.h"
17
18static thread_func acquire1_thread_func;
19static thread_func acquire2_thread_func;
20
21void
22test_priority_donate_one (void)
23{
24 struct lock lock;
25
26 /* This test does not work with the MLFQS. */
27 ASSERT (!thread_mlfqs);
28
29 /* Make sure our priority is the default. */
30 ASSERT (thread_get_priority () == PRI_DEFAULT);
31
32 lock_init (&lock);
33 lock_acquire (&lock);
34 thread_create ("acquire1", PRI_DEFAULT + 1, acquire1_thread_func, &lock);
35 msg ("This thread should have priority %d. Actual priority: %d.",
36 PRI_DEFAULT + 1, thread_get_priority ());
37 thread_create ("acquire2", PRI_DEFAULT + 2, acquire2_thread_func, &lock);
38 msg ("This thread should have priority %d. Actual priority: %d.",
39 PRI_DEFAULT + 2, thread_get_priority ());
40 lock_release (&lock);
41 msg ("acquire2, acquire1 must already have finished, in that order.");
42 msg ("This should be the last line before finishing this test.");
43}
44
45static void
46acquire1_thread_func (void *lock_)
47{
48 struct lock *lock = lock_;
49
50 lock_acquire (lock);
51 msg ("acquire1: got the lock");
52 lock_release (lock);
53 msg ("acquire1: done");
54}
55
56static void
57acquire2_thread_func (void *lock_)
58{
59 struct lock *lock = lock_;
60
61 lock_acquire (lock);
62 msg ("acquire2: got the lock");
63 lock_release (lock);
64 msg ("acquire2: done");
65}
diff --git a/pintos-progos/tests/threads/priority-donate-one.ck b/pintos-progos/tests/threads/priority-donate-one.ck
new file mode 100644
index 0000000..b7c8e6f
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-one.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-one) begin
7(priority-donate-one) This thread should have priority 32. Actual priority: 32.
8(priority-donate-one) This thread should have priority 33. Actual priority: 33.
9(priority-donate-one) acquire2: got the lock
10(priority-donate-one) acquire2: done
11(priority-donate-one) acquire1: got the lock
12(priority-donate-one) acquire1: done
13(priority-donate-one) acquire2, acquire1 must already have finished, in that order.
14(priority-donate-one) This should be the last line before finishing this test.
15(priority-donate-one) end
16EOF
17pass;
diff --git a/pintos-progos/tests/threads/priority-donate-sema.c b/pintos-progos/tests/threads/priority-donate-sema.c
new file mode 100644
index 0000000..b33cb72
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-sema.c
@@ -0,0 +1,82 @@
1/* Low priority thread L acquires a lock, then blocks downing a
2 semaphore. Medium priority thread M then blocks waiting on
3 the same semaphore. Next, high priority thread H attempts to
4 acquire the lock, donating its priority to L.
5
6 Next, the main thread ups the semaphore, waking up L. L
7 releases the lock, which wakes up H. H "up"s the semaphore,
8 waking up M. H terminates, then M, then L, and finally the
9 main thread.
10
11 Written by Godmar Back <gback@cs.vt.edu>. */
12
13#include <stdio.h>
14#include "tests/threads/tests.h"
15#include "threads/init.h"
16#include "threads/synch.h"
17#include "threads/thread.h"
18
19struct lock_and_sema
20 {
21 struct lock lock;
22 struct semaphore sema;
23 };
24
25static thread_func l_thread_func;
26static thread_func m_thread_func;
27static thread_func h_thread_func;
28
29void
30test_priority_donate_sema (void)
31{
32 struct lock_and_sema ls;
33
34 /* This test does not work with the MLFQS. */
35 ASSERT (!thread_mlfqs);
36
37 /* Make sure our priority is the default. */
38 ASSERT (thread_get_priority () == PRI_DEFAULT);
39
40 lock_init (&ls.lock);
41 sema_init (&ls.sema, 0);
42 thread_create ("low", PRI_DEFAULT + 1, l_thread_func, &ls);
43 thread_create ("med", PRI_DEFAULT + 3, m_thread_func, &ls);
44 thread_create ("high", PRI_DEFAULT + 5, h_thread_func, &ls);
45 sema_up (&ls.sema);
46 msg ("Main thread finished.");
47}
48
49static void
50l_thread_func (void *ls_)
51{
52 struct lock_and_sema *ls = ls_;
53
54 lock_acquire (&ls->lock);
55 msg ("Thread L acquired lock.");
56 sema_down (&ls->sema);
57 msg ("Thread L downed semaphore.");
58 lock_release (&ls->lock);
59 msg ("Thread L finished.");
60}
61
62static void
63m_thread_func (void *ls_)
64{
65 struct lock_and_sema *ls = ls_;
66
67 sema_down (&ls->sema);
68 msg ("Thread M finished.");
69}
70
71static void
72h_thread_func (void *ls_)
73{
74 struct lock_and_sema *ls = ls_;
75
76 lock_acquire (&ls->lock);
77 msg ("Thread H acquired lock.");
78
79 sema_up (&ls->sema);
80 lock_release (&ls->lock);
81 msg ("Thread H finished.");
82}
diff --git a/pintos-progos/tests/threads/priority-donate-sema.ck b/pintos-progos/tests/threads/priority-donate-sema.ck
new file mode 100644
index 0000000..92b8d07
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-donate-sema.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-donate-sema) begin
7(priority-donate-sema) Thread L acquired lock.
8(priority-donate-sema) Thread L downed semaphore.
9(priority-donate-sema) Thread H acquired lock.
10(priority-donate-sema) Thread H finished.
11(priority-donate-sema) Thread M finished.
12(priority-donate-sema) Thread L finished.
13(priority-donate-sema) Main thread finished.
14(priority-donate-sema) end
15EOF
16pass;
diff --git a/pintos-progos/tests/threads/priority-fifo.c b/pintos-progos/tests/threads/priority-fifo.c
new file mode 100644
index 0000000..3af98a3
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-fifo.c
@@ -0,0 +1,99 @@
1/* Creates several threads all at the same priority and ensures
2 that they consistently run in the same round-robin order.
3
4 Based on a test originally submitted for Stanford's CS 140 in
5 winter 1999 by by Matt Franklin
6 <startled@leland.stanford.edu>, Greg Hutchins
7 <gmh@leland.stanford.edu>, Yu Ping Hu <yph@cs.stanford.edu>.
8 Modified by arens. */
9
10#include <stdio.h>
11#include "tests/threads/tests.h"
12#include "threads/init.h"
13#include "devices/timer.h"
14#include "threads/malloc.h"
15#include "threads/synch.h"
16#include "threads/thread.h"
17
18struct simple_thread_data
19 {
20 int id; /* Sleeper ID. */
21 int iterations; /* Iterations so far. */
22 struct lock *lock; /* Lock on output. */
23 int **op; /* Output buffer position. */
24 };
25
26#define THREAD_CNT 16
27#define ITER_CNT 16
28
29static thread_func simple_thread_func;
30
31void
32test_priority_fifo (void)
33{
34 struct simple_thread_data data[THREAD_CNT];
35 struct lock lock;
36 int *output, *op;
37 int i, cnt;
38
39 /* This test does not work with the MLFQS. */
40 ASSERT (!thread_mlfqs);
41
42 /* Make sure our priority is the default. */
43 ASSERT (thread_get_priority () == PRI_DEFAULT);
44
45 msg ("%d threads will iterate %d times in the same order each time.",
46 THREAD_CNT, ITER_CNT);
47 msg ("If the order varies then there is a bug.");
48
49 output = op = malloc (sizeof *output * THREAD_CNT * ITER_CNT * 2);
50 ASSERT (output != NULL);
51 lock_init (&lock);
52
53 thread_set_priority (PRI_DEFAULT + 2);
54 for (i = 0; i < THREAD_CNT; i++)
55 {
56 char name[16];
57 struct simple_thread_data *d = data + i;
58 snprintf (name, sizeof name, "%d", i);
59 d->id = i;
60 d->iterations = 0;
61 d->lock = &lock;
62 d->op = &op;
63 thread_create (name, PRI_DEFAULT + 1, simple_thread_func, d);
64 }
65
66 thread_set_priority (PRI_DEFAULT);
67 /* All the other threads now run to termination here. */
68 ASSERT (lock.holder == NULL);
69
70 cnt = 0;
71 for (; output < op; output++)
72 {
73 struct simple_thread_data *d;
74
75 ASSERT (*output >= 0 && *output < THREAD_CNT);
76 d = data + *output;
77 if (cnt % THREAD_CNT == 0)
78 printf ("(priority-fifo) iteration:");
79 printf (" %d", d->id);
80 if (++cnt % THREAD_CNT == 0)
81 printf ("\n");
82 d->iterations++;
83 }
84}
85
86static void
87simple_thread_func (void *data_)
88{
89 struct simple_thread_data *data = data_;
90 int i;
91
92 for (i = 0; i < ITER_CNT; i++)
93 {
94 lock_acquire (data->lock);
95 *(*data->op)++ = data->id;
96 lock_release (data->lock);
97 thread_yield ();
98 }
99}
diff --git a/pintos-progos/tests/threads/priority-fifo.ck b/pintos-progos/tests/threads/priority-fifo.ck
new file mode 100644
index 0000000..11f1dd3
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-fifo.ck
@@ -0,0 +1,63 @@
1# -*- perl -*-
2
3# The expected output looks like this:
4#
5# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
6# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
7# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
8# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
9# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
10# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
11# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
12# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
13# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
14# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
15# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
17# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
18# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
19# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
20# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
21#
22# A different permutation of 0...15 is acceptable, but every line must
23# be in the same order.
24
25use strict;
26use warnings;
27use tests::tests;
28
29our ($test);
30my (@output) = read_text_file ("$test.output");
31
32common_checks ("run", @output);
33
34my ($thread_cnt) = 16;
35my ($iter_cnt) = 16;
36my (@order);
37my (@t) = (-1) x $thread_cnt;
38
39my (@iterations) = grep (/iteration:/, @output);
40fail "No iterations found in output.\n" if !@iterations;
41
42my (@numbering) = $iterations[0] =~ /(\d+)/g;
43fail "First iteration does not list exactly $thread_cnt threads.\n"
44 if @numbering != $thread_cnt;
45
46my (@sorted_numbering) = sort { $a <=> $b } @numbering;
47for my $i (0...$#sorted_numbering) {
48 if ($sorted_numbering[$i] != $i) {
49 fail "First iteration does not list all threads "
50 . "0...$#sorted_numbering\n";
51 }
52}
53
54for my $i (1...$#iterations) {
55 if ($iterations[$i] ne $iterations[0]) {
56 fail "Iteration $i differs from iteration 0\n";
57 }
58}
59
60fail "$iter_cnt iterations expected but " . scalar (@iterations) . " found\n"
61 if $iter_cnt != @iterations;
62
63pass;
diff --git a/pintos-progos/tests/threads/priority-preempt.c b/pintos-progos/tests/threads/priority-preempt.c
new file mode 100644
index 0000000..3c3aacb
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-preempt.c
@@ -0,0 +1,41 @@
1/* Ensures that a high-priority thread really preempts.
2
3 Based on a test originally submitted for Stanford's CS 140 in
4 winter 1999 by by Matt Franklin
5 <startled@leland.stanford.edu>, Greg Hutchins
6 <gmh@leland.stanford.edu>, Yu Ping Hu <yph@cs.stanford.edu>.
7 Modified by arens. */
8
9#include <stdio.h>
10#include "tests/threads/tests.h"
11#include "threads/init.h"
12#include "threads/synch.h"
13#include "threads/thread.h"
14
15static thread_func simple_thread_func;
16
17void
18test_priority_preempt (void)
19{
20 /* This test does not work with the MLFQS. */
21 ASSERT (!thread_mlfqs);
22
23 /* Make sure our priority is the default. */
24 ASSERT (thread_get_priority () == PRI_DEFAULT);
25
26 thread_create ("high-priority", PRI_DEFAULT + 1, simple_thread_func, NULL);
27 msg ("The high-priority thread should have already completed.");
28}
29
30static void
31simple_thread_func (void *aux UNUSED)
32{
33 int i;
34
35 for (i = 0; i < 5; i++)
36 {
37 msg ("Thread %s iteration %d", thread_name (), i);
38 thread_yield ();
39 }
40 msg ("Thread %s done!", thread_name ());
41}
diff --git a/pintos-progos/tests/threads/priority-preempt.ck b/pintos-progos/tests/threads/priority-preempt.ck
new file mode 100644
index 0000000..43a26ee
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-preempt.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-preempt) begin
7(priority-preempt) Thread high-priority iteration 0
8(priority-preempt) Thread high-priority iteration 1
9(priority-preempt) Thread high-priority iteration 2
10(priority-preempt) Thread high-priority iteration 3
11(priority-preempt) Thread high-priority iteration 4
12(priority-preempt) Thread high-priority done!
13(priority-preempt) The high-priority thread should have already completed.
14(priority-preempt) end
15EOF
16pass;
diff --git a/pintos-progos/tests/threads/priority-sema.c b/pintos-progos/tests/threads/priority-sema.c
new file mode 100644
index 0000000..2834a88
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-sema.c
@@ -0,0 +1,45 @@
1/* Tests that the highest-priority thread waiting on a semaphore
2 is the first to wake up. */
3
4#include <stdio.h>
5#include "tests/threads/tests.h"
6#include "threads/init.h"
7#include "threads/malloc.h"
8#include "threads/synch.h"
9#include "threads/thread.h"
10#include "devices/timer.h"
11
12static thread_func priority_sema_thread;
13static struct semaphore sema;
14
15void
16test_priority_sema (void)
17{
18 int i;
19
20 /* This test does not work with the MLFQS. */
21 ASSERT (!thread_mlfqs);
22
23 sema_init (&sema, 0);
24 thread_set_priority (PRI_MIN);
25 for (i = 0; i < 10; i++)
26 {
27 int priority = PRI_DEFAULT - (i + 3) % 10 - 1;
28 char name[16];
29 snprintf (name, sizeof name, "priority %d", priority);
30 thread_create (name, priority, priority_sema_thread, NULL);
31 }
32
33 for (i = 0; i < 10; i++)
34 {
35 sema_up (&sema);
36 msg ("Back in main thread.");
37 }
38}
39
40static void
41priority_sema_thread (void *aux UNUSED)
42{
43 sema_down (&sema);
44 msg ("Thread %s woke up.", thread_name ());
45}
diff --git a/pintos-progos/tests/threads/priority-sema.ck b/pintos-progos/tests/threads/priority-sema.ck
new file mode 100644
index 0000000..559988d
--- /dev/null
+++ b/pintos-progos/tests/threads/priority-sema.ck
@@ -0,0 +1,29 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(priority-sema) begin
7(priority-sema) Thread priority 30 woke up.
8(priority-sema) Back in main thread.
9(priority-sema) Thread priority 29 woke up.
10(priority-sema) Back in main thread.
11(priority-sema) Thread priority 28 woke up.
12(priority-sema) Back in main thread.
13(priority-sema) Thread priority 27 woke up.
14(priority-sema) Back in main thread.
15(priority-sema) Thread priority 26 woke up.
16(priority-sema) Back in main thread.
17(priority-sema) Thread priority 25 woke up.
18(priority-sema) Back in main thread.
19(priority-sema) Thread priority 24 woke up.
20(priority-sema) Back in main thread.
21(priority-sema) Thread priority 23 woke up.
22(priority-sema) Back in main thread.
23(priority-sema) Thread priority 22 woke up.
24(priority-sema) Back in main thread.
25(priority-sema) Thread priority 21 woke up.
26(priority-sema) Back in main thread.
27(priority-sema) end
28EOF
29pass;
diff --git a/pintos-progos/tests/threads/tests.c b/pintos-progos/tests/threads/tests.c
new file mode 100644
index 0000000..af15aee
--- /dev/null
+++ b/pintos-progos/tests/threads/tests.c
@@ -0,0 +1,102 @@
1#include "tests/threads/tests.h"
2#include <debug.h>
3#include <string.h>
4#include <stdio.h>
5
6struct test
7 {
8 const char *name;
9 test_func *function;
10 };
11
12static const struct test tests[] =
13 {
14 {"alarm-single", test_alarm_single},
15 {"alarm-multiple", test_alarm_multiple},
16 {"alarm-simultaneous", test_alarm_simultaneous},
17 {"alarm-priority", test_alarm_priority},
18 {"alarm-zero", test_alarm_zero},
19 {"alarm-negative", test_alarm_negative},
20 {"priority-change", test_priority_change},
21 {"priority-donate-one", test_priority_donate_one},
22 {"priority-donate-multiple", test_priority_donate_multiple},
23 {"priority-donate-multiple2", test_priority_donate_multiple2},
24 {"priority-donate-nest", test_priority_donate_nest},
25 {"priority-donate-sema", test_priority_donate_sema},
26 {"priority-donate-lower", test_priority_donate_lower},
27 {"priority-donate-chain", test_priority_donate_chain},
28 {"priority-fifo", test_priority_fifo},
29 {"priority-preempt", test_priority_preempt},
30 {"priority-sema", test_priority_sema},
31 {"priority-condvar", test_priority_condvar},
32 {"mlfqs-load-1", test_mlfqs_load_1},
33 {"mlfqs-load-60", test_mlfqs_load_60},
34 {"mlfqs-load-avg", test_mlfqs_load_avg},
35 {"mlfqs-recent-1", test_mlfqs_recent_1},
36 {"mlfqs-fair-2", test_mlfqs_fair_2},
37 {"mlfqs-fair-20", test_mlfqs_fair_20},
38 {"mlfqs-nice-2", test_mlfqs_nice_2},
39 {"mlfqs-nice-10", test_mlfqs_nice_10},
40 {"mlfqs-block", test_mlfqs_block},
41 };
42
43static const char *test_name;
44
45/* Runs the test named NAME. */
46void
47run_test (const char *name)
48{
49 const struct test *t;
50
51 for (t = tests; t < tests + sizeof tests / sizeof *tests; t++)
52 if (!strcmp (name, t->name))
53 {
54 test_name = name;
55 msg ("begin");
56 t->function ();
57 msg ("end");
58 return;
59 }
60 PANIC ("no test named \"%s\"", name);
61}
62
63/* Prints FORMAT as if with printf(),
64 prefixing the output by the name of the test
65 and following it with a new-line character. */
66void
67msg (const char *format, ...)
68{
69 va_list args;
70
71 printf ("(%s) ", test_name);
72 va_start (args, format);
73 vprintf (format, args);
74 va_end (args);
75 putchar ('\n');
76}
77
78/* Prints failure message FORMAT as if with printf(),
79 prefixing the output by the name of the test and FAIL:
80 and following it with a new-line character,
81 and then panics the kernel. */
82void
83fail (const char *format, ...)
84{
85 va_list args;
86
87 printf ("(%s) FAIL: ", test_name);
88 va_start (args, format);
89 vprintf (format, args);
90 va_end (args);
91 putchar ('\n');
92
93 PANIC ("test failed");
94}
95
96/* Prints a message indicating the current test passed. */
97void
98pass (void)
99{
100 printf ("(%s) PASS\n", test_name);
101}
102
diff --git a/pintos-progos/tests/threads/tests.h b/pintos-progos/tests/threads/tests.h
new file mode 100644
index 0000000..cd9d489
--- /dev/null
+++ b/pintos-progos/tests/threads/tests.h
@@ -0,0 +1,41 @@
1#ifndef TESTS_THREADS_TESTS_H
2#define TESTS_THREADS_TESTS_H
3
4void run_test (const char *);
5
6typedef void test_func (void);
7
8extern test_func test_alarm_single;
9extern test_func test_alarm_multiple;
10extern test_func test_alarm_simultaneous;
11extern test_func test_alarm_priority;
12extern test_func test_alarm_zero;
13extern test_func test_alarm_negative;
14extern test_func test_priority_change;
15extern test_func test_priority_donate_one;
16extern test_func test_priority_donate_multiple;
17extern test_func test_priority_donate_multiple2;
18extern test_func test_priority_donate_sema;
19extern test_func test_priority_donate_nest;
20extern test_func test_priority_donate_lower;
21extern test_func test_priority_donate_chain;
22extern test_func test_priority_fifo;
23extern test_func test_priority_preempt;
24extern test_func test_priority_sema;
25extern test_func test_priority_condvar;
26extern test_func test_mlfqs_load_1;
27extern test_func test_mlfqs_load_60;
28extern test_func test_mlfqs_load_avg;
29extern test_func test_mlfqs_recent_1;
30extern test_func test_mlfqs_fair_2;
31extern test_func test_mlfqs_fair_20;
32extern test_func test_mlfqs_nice_2;
33extern test_func test_mlfqs_nice_10;
34extern test_func test_mlfqs_block;
35
36void msg (const char *, ...);
37void fail (const char *, ...);
38void pass (void);
39
40#endif /* tests/threads/tests.h */
41
diff --git a/pintos-progos/tests/userprog/Make.tests b/pintos-progos/tests/userprog/Make.tests
new file mode 100644
index 0000000..88b0737
--- /dev/null
+++ b/pintos-progos/tests/userprog/Make.tests
@@ -0,0 +1,133 @@
1# -*- makefile -*-
2
3tests/%.output: FILESYSSOURCE = --filesys-size=2
4tests/%.output: PUTFILES = $(filter-out kernel.bin loader.bin, $^)
5
6tests/userprog_TESTS = $(addprefix tests/userprog/,args-none \
7args-single args-multiple args-many args-dbl-space sc-bad-sp \
8sc-bad-arg sc-boundary sc-boundary-2 halt exit create-normal \
9create-empty create-null create-bad-ptr create-long create-exists \
10create-bound open-normal open-missing open-boundary open-empty \
11open-null open-bad-ptr open-twice close-normal close-twice close-stdin \
12close-stdout close-bad-fd read-normal read-bad-ptr read-boundary \
13read-zero read-stdout read-bad-fd write-normal write-bad-ptr \
14write-boundary write-zero write-stdin write-bad-fd exec-once exec-arg \
15exec-multiple exec-missing exec-bad-ptr wait-simple wait-twice \
16wait-killed wait-bad-pid multi-recurse multi-child-fd rox-simple \
17rox-child rox-multichild bad-read bad-write bad-read2 bad-write2 \
18bad-jump bad-jump2)
19
20tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \
21tests/userprog/,child-simple child-args child-bad child-close child-rox)
22
23tests/userprog/args-none_SRC = tests/userprog/args.c
24tests/userprog/args-single_SRC = tests/userprog/args.c
25tests/userprog/args-multiple_SRC = tests/userprog/args.c
26tests/userprog/args-many_SRC = tests/userprog/args.c
27tests/userprog/args-dbl-space_SRC = tests/userprog/args.c
28tests/userprog/sc-bad-sp_SRC = tests/userprog/sc-bad-sp.c tests/main.c
29tests/userprog/sc-bad-arg_SRC = tests/userprog/sc-bad-arg.c tests/main.c
30tests/userprog/bad-read_SRC = tests/userprog/bad-read.c tests/main.c
31tests/userprog/bad-write_SRC = tests/userprog/bad-write.c tests/main.c
32tests/userprog/bad-jump_SRC = tests/userprog/bad-jump.c tests/main.c
33tests/userprog/bad-read2_SRC = tests/userprog/bad-read2.c tests/main.c
34tests/userprog/bad-write2_SRC = tests/userprog/bad-write2.c tests/main.c
35tests/userprog/bad-jump2_SRC = tests/userprog/bad-jump2.c tests/main.c
36tests/userprog/sc-boundary_SRC = tests/userprog/sc-boundary.c \
37tests/userprog/boundary.c tests/main.c
38tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c \
39tests/userprog/boundary.c tests/main.c
40tests/userprog/halt_SRC = tests/userprog/halt.c tests/main.c
41tests/userprog/exit_SRC = tests/userprog/exit.c tests/main.c
42tests/userprog/create-normal_SRC = tests/userprog/create-normal.c tests/main.c
43tests/userprog/create-empty_SRC = tests/userprog/create-empty.c tests/main.c
44tests/userprog/create-null_SRC = tests/userprog/create-null.c tests/main.c
45tests/userprog/create-bad-ptr_SRC = tests/userprog/create-bad-ptr.c \
46tests/main.c
47tests/userprog/create-long_SRC = tests/userprog/create-long.c tests/main.c
48tests/userprog/create-exists_SRC = tests/userprog/create-exists.c tests/main.c
49tests/userprog/create-bound_SRC = tests/userprog/create-bound.c \
50tests/userprog/boundary.c tests/main.c
51tests/userprog/open-normal_SRC = tests/userprog/open-normal.c tests/main.c
52tests/userprog/open-missing_SRC = tests/userprog/open-missing.c tests/main.c
53tests/userprog/open-boundary_SRC = tests/userprog/open-boundary.c \
54tests/userprog/boundary.c tests/main.c
55tests/userprog/open-empty_SRC = tests/userprog/open-empty.c tests/main.c
56tests/userprog/open-null_SRC = tests/userprog/open-null.c tests/main.c
57tests/userprog/open-bad-ptr_SRC = tests/userprog/open-bad-ptr.c tests/main.c
58tests/userprog/open-twice_SRC = tests/userprog/open-twice.c tests/main.c
59tests/userprog/close-normal_SRC = tests/userprog/close-normal.c tests/main.c
60tests/userprog/close-twice_SRC = tests/userprog/close-twice.c tests/main.c
61tests/userprog/close-stdin_SRC = tests/userprog/close-stdin.c tests/main.c
62tests/userprog/close-stdout_SRC = tests/userprog/close-stdout.c tests/main.c
63tests/userprog/close-bad-fd_SRC = tests/userprog/close-bad-fd.c tests/main.c
64tests/userprog/read-normal_SRC = tests/userprog/read-normal.c tests/main.c
65tests/userprog/read-bad-ptr_SRC = tests/userprog/read-bad-ptr.c tests/main.c
66tests/userprog/read-boundary_SRC = tests/userprog/read-boundary.c \
67tests/userprog/boundary.c tests/main.c
68tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c
69tests/userprog/read-stdout_SRC = tests/userprog/read-stdout.c tests/main.c
70tests/userprog/read-bad-fd_SRC = tests/userprog/read-bad-fd.c tests/main.c
71tests/userprog/write-normal_SRC = tests/userprog/write-normal.c tests/main.c
72tests/userprog/write-bad-ptr_SRC = tests/userprog/write-bad-ptr.c tests/main.c
73tests/userprog/write-boundary_SRC = tests/userprog/write-boundary.c \
74tests/userprog/boundary.c tests/main.c
75tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c
76tests/userprog/write-stdin_SRC = tests/userprog/write-stdin.c tests/main.c
77tests/userprog/write-bad-fd_SRC = tests/userprog/write-bad-fd.c tests/main.c
78tests/userprog/exec-once_SRC = tests/userprog/exec-once.c tests/main.c
79tests/userprog/exec-arg_SRC = tests/userprog/exec-arg.c tests/main.c
80tests/userprog/exec-multiple_SRC = tests/userprog/exec-multiple.c tests/main.c
81tests/userprog/exec-missing_SRC = tests/userprog/exec-missing.c tests/main.c
82tests/userprog/exec-bad-ptr_SRC = tests/userprog/exec-bad-ptr.c tests/main.c
83tests/userprog/wait-simple_SRC = tests/userprog/wait-simple.c tests/main.c
84tests/userprog/wait-twice_SRC = tests/userprog/wait-twice.c tests/main.c
85tests/userprog/wait-killed_SRC = tests/userprog/wait-killed.c tests/main.c
86tests/userprog/wait-bad-pid_SRC = tests/userprog/wait-bad-pid.c tests/main.c
87tests/userprog/multi-recurse_SRC = tests/userprog/multi-recurse.c
88tests/userprog/multi-child-fd_SRC = tests/userprog/multi-child-fd.c \
89tests/main.c
90tests/userprog/rox-simple_SRC = tests/userprog/rox-simple.c tests/main.c
91tests/userprog/rox-child_SRC = tests/userprog/rox-child.c tests/main.c
92tests/userprog/rox-multichild_SRC = tests/userprog/rox-multichild.c \
93tests/main.c
94
95tests/userprog/child-simple_SRC = tests/userprog/child-simple.c
96tests/userprog/child-args_SRC = tests/userprog/args.c
97tests/userprog/child-bad_SRC = tests/userprog/child-bad.c tests/main.c
98tests/userprog/child-close_SRC = tests/userprog/child-close.c
99tests/userprog/child-rox_SRC = tests/userprog/child-rox.c
100
101$(foreach prog,$(tests/userprog_PROGS),$(eval $(prog)_SRC += tests/lib.c))
102
103tests/userprog/args-single_ARGS = onearg
104tests/userprog/args-multiple_ARGS = some arguments for you!
105tests/userprog/args-many_ARGS = a b c d e f g h i j k l m n o p q r s t u v
106tests/userprog/args-dbl-space_ARGS = two spaces!
107tests/userprog/multi-recurse_ARGS = 15
108
109tests/userprog/open-normal_PUTFILES += tests/userprog/sample.txt
110tests/userprog/open-boundary_PUTFILES += tests/userprog/sample.txt
111tests/userprog/open-twice_PUTFILES += tests/userprog/sample.txt
112tests/userprog/close-normal_PUTFILES += tests/userprog/sample.txt
113tests/userprog/close-twice_PUTFILES += tests/userprog/sample.txt
114tests/userprog/read-normal_PUTFILES += tests/userprog/sample.txt
115tests/userprog/read-bad-ptr_PUTFILES += tests/userprog/sample.txt
116tests/userprog/read-boundary_PUTFILES += tests/userprog/sample.txt
117tests/userprog/read-zero_PUTFILES += tests/userprog/sample.txt
118tests/userprog/write-normal_PUTFILES += tests/userprog/sample.txt
119tests/userprog/write-bad-ptr_PUTFILES += tests/userprog/sample.txt
120tests/userprog/write-boundary_PUTFILES += tests/userprog/sample.txt
121tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt
122tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt
123
124tests/userprog/exec-once_PUTFILES += tests/userprog/child-simple
125tests/userprog/exec-multiple_PUTFILES += tests/userprog/child-simple
126tests/userprog/wait-simple_PUTFILES += tests/userprog/child-simple
127tests/userprog/wait-twice_PUTFILES += tests/userprog/child-simple
128
129tests/userprog/exec-arg_PUTFILES += tests/userprog/child-args
130tests/userprog/multi-child-fd_PUTFILES += tests/userprog/child-close
131tests/userprog/wait-killed_PUTFILES += tests/userprog/child-bad
132tests/userprog/rox-child_PUTFILES += tests/userprog/child-rox
133tests/userprog/rox-multichild_PUTFILES += tests/userprog/child-rox
diff --git a/pintos-progos/tests/userprog/Rubric.functionality b/pintos-progos/tests/userprog/Rubric.functionality
new file mode 100644
index 0000000..ea76c44
--- /dev/null
+++ b/pintos-progos/tests/userprog/Rubric.functionality
@@ -0,0 +1,52 @@
1Functionality of system calls:
2- Test argument passing on Pintos command line.
33 args-none
43 args-single
53 args-multiple
63 args-many
73 args-dbl-space
8
9- Test "create" system call.
103 create-empty
113 create-long
123 create-normal
133 create-exists
14
15- Test "open" system call.
163 open-missing
173 open-normal
183 open-twice
19
20- Test "read" system call.
213 read-normal
223 read-zero
23
24- Test "write" system call.
253 write-normal
263 write-zero
27
28- Test "close" system call.
293 close-normal
30
31- Test "exec" system call.
325 exec-once
335 exec-multiple
345 exec-arg
35
36- Test "wait" system call.
375 wait-simple
385 wait-twice
39
40- Test "exit" system call.
415 exit
42
43- Test "halt" system call.
443 halt
45
46- Test recursive execution of user programs.
4715 multi-recurse
48
49- Test read-only executable feature.
503 rox-simple
513 rox-child
523 rox-multichild
diff --git a/pintos-progos/tests/userprog/Rubric.robustness b/pintos-progos/tests/userprog/Rubric.robustness
new file mode 100644
index 0000000..b7d1035
--- /dev/null
+++ b/pintos-progos/tests/userprog/Rubric.robustness
@@ -0,0 +1,48 @@
1Robustness of system calls:
2- Test robustness of file descriptor handling.
32 close-stdin
42 close-stdout
52 close-bad-fd
62 close-twice
72 read-bad-fd
82 read-stdout
92 write-bad-fd
102 write-stdin
112 multi-child-fd
12
13- Test robustness of pointer handling.
143 create-bad-ptr
153 exec-bad-ptr
163 open-bad-ptr
173 read-bad-ptr
183 write-bad-ptr
19
20- Test robustness of buffer copying across page boundaries.
213 create-bound
223 open-boundary
233 read-boundary
243 write-boundary
25
26- Test handling of null pointer and empty strings.
272 create-null
282 open-null
292 open-empty
30
31- Test robustness of system call implementation.
323 sc-bad-arg
333 sc-bad-sp
345 sc-boundary
355 sc-boundary-2
36
37- Test robustness of "exec" and "wait" system calls.
385 exec-missing
395 wait-bad-pid
405 wait-killed
41
42- Test robustness of exception handling.
431 bad-read
441 bad-write
451 bad-jump
461 bad-read2
471 bad-write2
481 bad-jump2
diff --git a/pintos-progos/tests/userprog/args-dbl-space.ck b/pintos-progos/tests/userprog/args-dbl-space.ck
new file mode 100644
index 0000000..dfbcf4b
--- /dev/null
+++ b/pintos-progos/tests/userprog/args-dbl-space.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(args) begin
7(args) argc = 3
8(args) argv[0] = 'args-dbl-space'
9(args) argv[1] = 'two'
10(args) argv[2] = 'spaces!'
11(args) argv[3] = null
12(args) end
13args-dbl-space: exit(0)
14EOF
15pass;
diff --git a/pintos-progos/tests/userprog/args-many.ck b/pintos-progos/tests/userprog/args-many.ck
new file mode 100644
index 0000000..214574a
--- /dev/null
+++ b/pintos-progos/tests/userprog/args-many.ck
@@ -0,0 +1,35 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(args) begin
7(args) argc = 23
8(args) argv[0] = 'args-many'
9(args) argv[1] = 'a'
10(args) argv[2] = 'b'
11(args) argv[3] = 'c'
12(args) argv[4] = 'd'
13(args) argv[5] = 'e'
14(args) argv[6] = 'f'
15(args) argv[7] = 'g'
16(args) argv[8] = 'h'
17(args) argv[9] = 'i'
18(args) argv[10] = 'j'
19(args) argv[11] = 'k'
20(args) argv[12] = 'l'
21(args) argv[13] = 'm'
22(args) argv[14] = 'n'
23(args) argv[15] = 'o'
24(args) argv[16] = 'p'
25(args) argv[17] = 'q'
26(args) argv[18] = 'r'
27(args) argv[19] = 's'
28(args) argv[20] = 't'
29(args) argv[21] = 'u'
30(args) argv[22] = 'v'
31(args) argv[23] = null
32(args) end
33args-many: exit(0)
34EOF
35pass;
diff --git a/pintos-progos/tests/userprog/args-multiple.ck b/pintos-progos/tests/userprog/args-multiple.ck
new file mode 100644
index 0000000..227e6cc
--- /dev/null
+++ b/pintos-progos/tests/userprog/args-multiple.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(args) begin
7(args) argc = 5
8(args) argv[0] = 'args-multiple'
9(args) argv[1] = 'some'
10(args) argv[2] = 'arguments'
11(args) argv[3] = 'for'
12(args) argv[4] = 'you!'
13(args) argv[5] = null
14(args) end
15args-multiple: exit(0)
16EOF
17pass;
diff --git a/pintos-progos/tests/userprog/args-none.ck b/pintos-progos/tests/userprog/args-none.ck
new file mode 100644
index 0000000..146318e
--- /dev/null
+++ b/pintos-progos/tests/userprog/args-none.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(args) begin
7(args) argc = 1
8(args) argv[0] = 'args-none'
9(args) argv[1] = null
10(args) end
11args-none: exit(0)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/args-single.ck b/pintos-progos/tests/userprog/args-single.ck
new file mode 100644
index 0000000..24582b4
--- /dev/null
+++ b/pintos-progos/tests/userprog/args-single.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(args) begin
7(args) argc = 2
8(args) argv[0] = 'args-single'
9(args) argv[1] = 'onearg'
10(args) argv[2] = null
11(args) end
12args-single: exit(0)
13EOF
14pass;
diff --git a/pintos-progos/tests/userprog/args.c b/pintos-progos/tests/userprog/args.c
new file mode 100644
index 0000000..20eda44
--- /dev/null
+++ b/pintos-progos/tests/userprog/args.c
@@ -0,0 +1,25 @@
1/* Prints the command-line arguments.
2 This program is used for all of the args-* tests. Grading is
3 done differently for each of the args-* tests based on the
4 output. */
5
6#include "tests/lib.h"
7
8int
9main (int argc, char *argv[])
10{
11 int i;
12
13 test_name = "args";
14
15 msg ("begin");
16 msg ("argc = %d", argc);
17 for (i = 0; i <= argc; i++)
18 if (argv[i] != NULL)
19 msg ("argv[%d] = '%s'", i, argv[i]);
20 else
21 msg ("argv[%d] = null", i);
22 msg ("end");
23
24 return 0;
25}
diff --git a/pintos-progos/tests/userprog/bad-jump.c b/pintos-progos/tests/userprog/bad-jump.c
new file mode 100644
index 0000000..51b7c9f
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-jump.c
@@ -0,0 +1,13 @@
1/* This program attempts to execute code at address 0, which is not mapped.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("Congratulations - you have successfully called NULL: %d",
11 ((int (*)(void))NULL)());
12 fail ("should have exited with -1");
13}
diff --git a/pintos-progos/tests/userprog/bad-jump.ck b/pintos-progos/tests/userprog/bad-jump.ck
new file mode 100644
index 0000000..e1c178b
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-jump.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-jump) begin
7bad-jump: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/bad-jump2.c b/pintos-progos/tests/userprog/bad-jump2.c
new file mode 100644
index 0000000..dc7c2a7
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-jump2.c
@@ -0,0 +1,13 @@
1/* This program attempts to execute code at a kernel virtual address.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("Congratulations - you have successfully called kernel code: %d",
11 ((int (*)(void))0xC0000000)());
12 fail ("should have exited with -1");
13}
diff --git a/pintos-progos/tests/userprog/bad-jump2.ck b/pintos-progos/tests/userprog/bad-jump2.ck
new file mode 100644
index 0000000..35f0f97
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-jump2.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-jump2) begin
7bad-jump2: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/bad-read.c b/pintos-progos/tests/userprog/bad-read.c
new file mode 100644
index 0000000..904c278
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-read.c
@@ -0,0 +1,13 @@
1/* This program attempts to read memory at an address that is not mapped.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("Congratulations - you have successfully dereferenced NULL: %d",
11 *(int *)NULL);
12 fail ("should have exited with -1");
13}
diff --git a/pintos-progos/tests/userprog/bad-read.ck b/pintos-progos/tests/userprog/bad-read.ck
new file mode 100644
index 0000000..4d4d926
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-read.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-read) begin
7bad-read: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/bad-read2.c b/pintos-progos/tests/userprog/bad-read2.c
new file mode 100644
index 0000000..a2fc237
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-read2.c
@@ -0,0 +1,13 @@
1/* This program attempts to read kernel memory.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("Congratulations - you have successfully read kernel memory: %d",
11 *(int *)0xC0000000);
12 fail ("should have exited with -1");
13}
diff --git a/pintos-progos/tests/userprog/bad-read2.ck b/pintos-progos/tests/userprog/bad-read2.ck
new file mode 100644
index 0000000..fa27c7d
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-read2.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-read2) begin
7bad-read2: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/bad-write.c b/pintos-progos/tests/userprog/bad-write.c
new file mode 100644
index 0000000..000c26b
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-write.c
@@ -0,0 +1,12 @@
1/* This program attempts to write to memory at an address that is not mapped.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 *(int *)NULL = 42;
11 fail ("should have exited with -1");
12}
diff --git a/pintos-progos/tests/userprog/bad-write.ck b/pintos-progos/tests/userprog/bad-write.ck
new file mode 100644
index 0000000..d213b49
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-write.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-write) begin
7bad-write: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/bad-write2.c b/pintos-progos/tests/userprog/bad-write2.c
new file mode 100644
index 0000000..753da1e
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-write2.c
@@ -0,0 +1,12 @@
1/* This program attempts to write to kernel memory.
2 This should terminate the process with a -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 *(int *)0xC0000000 = 42;
11 fail ("should have exited with -1");
12}
diff --git a/pintos-progos/tests/userprog/bad-write2.ck b/pintos-progos/tests/userprog/bad-write2.ck
new file mode 100644
index 0000000..c6a3420
--- /dev/null
+++ b/pintos-progos/tests/userprog/bad-write2.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(bad-write2) begin
7bad-write2: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/boundary.c b/pintos-progos/tests/userprog/boundary.c
new file mode 100644
index 0000000..59907ec
--- /dev/null
+++ b/pintos-progos/tests/userprog/boundary.c
@@ -0,0 +1,33 @@
1/* Utility function for tests that try to break system calls by
2 passing them data that crosses from one virtual page to
3 another. */
4
5#include <inttypes.h>
6#include <round.h>
7#include <string.h>
8#include "tests/userprog/boundary.h"
9
10static char dst[8192];
11
12/* Returns the beginning of a page. There are at least 2048
13 modifiable bytes on either side of the pointer returned. */
14void *
15get_boundary_area (void)
16{
17 char *p = (char *) ROUND_UP ((uintptr_t) dst, 4096);
18 if (p - dst < 2048)
19 p += 4096;
20 return p;
21}
22
23/* Returns a copy of SRC split across the boundary between two
24 pages. */
25char *
26copy_string_across_boundary (const char *src)
27{
28 char *p = get_boundary_area ();
29 p -= strlen (src) < 4096 ? strlen (src) / 2 : 4096;
30 strlcpy (p, src, 4096);
31 return p;
32}
33
diff --git a/pintos-progos/tests/userprog/boundary.h b/pintos-progos/tests/userprog/boundary.h
new file mode 100644
index 0000000..c8e4b3b
--- /dev/null
+++ b/pintos-progos/tests/userprog/boundary.h
@@ -0,0 +1,7 @@
1#ifndef TESTS_USERPROG_BOUNDARY_H
2#define TESTS_USERPROG_BOUNDARY_H
3
4void *get_boundary_area (void);
5char *copy_string_across_boundary (const char *);
6
7#endif /* tests/userprog/boundary.h */
diff --git a/pintos-progos/tests/userprog/child-bad.c b/pintos-progos/tests/userprog/child-bad.c
new file mode 100644
index 0000000..77d7a69
--- /dev/null
+++ b/pintos-progos/tests/userprog/child-bad.c
@@ -0,0 +1,14 @@
1/* Child process run by wait-killed test.
2 Sets the stack pointer (%esp) to an invalid value and invokes
3 a system call, which should then terminate the process with a
4 -1 exit code. */
5
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 asm volatile ("movl $0x20101234, %esp; int $0x30");
13 fail ("should have exited with -1");
14}
diff --git a/pintos-progos/tests/userprog/child-close.c b/pintos-progos/tests/userprog/child-close.c
new file mode 100644
index 0000000..ac948c8
--- /dev/null
+++ b/pintos-progos/tests/userprog/child-close.c
@@ -0,0 +1,28 @@
1/* Child process run by multi-child-fd test.
2
3 Attempts to close the file descriptor passed as the first
4 command-line argument. This is invalid, because file
5 descriptors are not inherited in Pintos. Two results are
6 allowed: either the system call should return without taking
7 any action, or the kernel should terminate the process with a
8 -1 exit code. */
9
10#include <ctype.h>
11#include <stdio.h>
12#include <stdlib.h>
13#include <syscall.h>
14#include "tests/lib.h"
15
16const char *test_name = "child-close";
17
18int
19main (int argc UNUSED, char *argv[])
20{
21 msg ("begin");
22 if (!isdigit (*argv[1]))
23 fail ("bad command-line arguments");
24 close (atoi (argv[1]));
25 msg ("end");
26
27 return 0;
28}
diff --git a/pintos-progos/tests/userprog/child-rox.c b/pintos-progos/tests/userprog/child-rox.c
new file mode 100644
index 0000000..aba808b
--- /dev/null
+++ b/pintos-progos/tests/userprog/child-rox.c
@@ -0,0 +1,55 @@
1/* Child process run by rox-child and rox-multichild tests.
2 Opens and tries to write to its own executable, verifying that
3 that is disallowed.
4 Then recursively executes itself to the depth indicated by the
5 first command-line argument. */
6
7#include <ctype.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <syscall.h>
11#include "tests/lib.h"
12
13const char *test_name = "child-rox";
14
15static void
16try_write (void)
17{
18 int handle;
19 char buffer[19];
20
21 quiet = true;
22 CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\"");
23 quiet = false;
24
25 CHECK (write (handle, buffer, sizeof buffer) == 0,
26 "try to write \"child-rox\"");
27
28 close (handle);
29}
30
31int
32main (int argc UNUSED, char *argv[])
33{
34 msg ("begin");
35 try_write ();
36
37 if (!isdigit (*argv[1]))
38 fail ("bad command-line arguments");
39 if (atoi (argv[1]) > 1)
40 {
41 char cmd[128];
42 int child;
43
44 snprintf (cmd, sizeof cmd, "child-rox %d", atoi (argv[1]) - 1);
45 CHECK ((child = exec (cmd)) != -1, "exec \"%s\"", cmd);
46 quiet = true;
47 CHECK (wait (child) == 12, "wait for \"child-rox\"");
48 quiet = false;
49 }
50
51 try_write ();
52 msg ("end");
53
54 return 12;
55}
diff --git a/pintos-progos/tests/userprog/child-simple.c b/pintos-progos/tests/userprog/child-simple.c
new file mode 100644
index 0000000..0d2dacf
--- /dev/null
+++ b/pintos-progos/tests/userprog/child-simple.c
@@ -0,0 +1,15 @@
1/* Child process run by exec-multiple, exec-one, wait-simple, and
2 wait-twice tests.
3 Just prints a single message and terminates. */
4
5#include <stdio.h>
6#include "tests/lib.h"
7
8const char *test_name = "child-simple";
9
10int
11main (void)
12{
13 msg ("run");
14 return 81;
15}
diff --git a/pintos-progos/tests/userprog/close-bad-fd.c b/pintos-progos/tests/userprog/close-bad-fd.c
new file mode 100644
index 0000000..f63bb9a
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-bad-fd.c
@@ -0,0 +1,11 @@
1/* Tries to close an invalid fd, which must either fail silently
2 or terminate with exit code -1. */
3
4#include <syscall.h>
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 close (0x20101234);
11}
diff --git a/pintos-progos/tests/userprog/close-bad-fd.ck b/pintos-progos/tests/userprog/close-bad-fd.ck
new file mode 100644
index 0000000..497b17c
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-bad-fd.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(close-bad-fd) begin
7(close-bad-fd) end
8close-bad-fd: exit(0)
9EOF
10(close-bad-fd) begin
11close-bad-fd: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/close-normal.c b/pintos-progos/tests/userprog/close-normal.c
new file mode 100644
index 0000000..8ce04e3
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-normal.c
@@ -0,0 +1,14 @@
1/* Opens a file and then closes it. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle;
11 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
12 msg ("close \"sample.txt\"");
13 close (handle);
14}
diff --git a/pintos-progos/tests/userprog/close-normal.ck b/pintos-progos/tests/userprog/close-normal.ck
new file mode 100644
index 0000000..fe41342
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-normal.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(close-normal) begin
7(close-normal) open "sample.txt"
8(close-normal) close "sample.txt"
9(close-normal) end
10close-normal: exit(0)
11EOF
12pass;
diff --git a/pintos-progos/tests/userprog/close-stdin.c b/pintos-progos/tests/userprog/close-stdin.c
new file mode 100644
index 0000000..9bbf9f2
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-stdin.c
@@ -0,0 +1,11 @@
1/* Tries to close the keyboard input stream, which must either
2 fail silently or terminate with exit code -1. */
3
4#include <syscall.h>
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 close (0);
11}
diff --git a/pintos-progos/tests/userprog/close-stdin.ck b/pintos-progos/tests/userprog/close-stdin.ck
new file mode 100644
index 0000000..3d28507
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-stdin.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(close-stdin) begin
7(close-stdin) end
8close-stdin: exit(0)
9EOF
10(close-stdin) begin
11close-stdin: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/close-stdout.c b/pintos-progos/tests/userprog/close-stdout.c
new file mode 100644
index 0000000..886523f
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-stdout.c
@@ -0,0 +1,11 @@
1/* Tries to close the console output stream, which must either
2 fail silently or terminate with exit code -1. */
3
4#include <syscall.h>
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 close (1);
11}
diff --git a/pintos-progos/tests/userprog/close-stdout.ck b/pintos-progos/tests/userprog/close-stdout.ck
new file mode 100644
index 0000000..3cbbcff
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-stdout.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(close-stdout) begin
7(close-stdout) end
8close-stdout: exit(0)
9EOF
10(close-stdout) begin
11close-stdout: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/close-twice.c b/pintos-progos/tests/userprog/close-twice.c
new file mode 100644
index 0000000..830bccf
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-twice.c
@@ -0,0 +1,18 @@
1/* Opens a file and then tries to close it twice. The second
2 close must either fail silently or terminate with exit code
3 -1. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 int handle;
13 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
14 msg ("close \"sample.txt\"");
15 close (handle);
16 msg ("close \"sample.txt\" again");
17 close (handle);
18}
diff --git a/pintos-progos/tests/userprog/close-twice.ck b/pintos-progos/tests/userprog/close-twice.ck
new file mode 100644
index 0000000..deb55a6
--- /dev/null
+++ b/pintos-progos/tests/userprog/close-twice.ck
@@ -0,0 +1,19 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(close-twice) begin
7(close-twice) open "sample.txt"
8(close-twice) close "sample.txt"
9(close-twice) close "sample.txt" again
10(close-twice) end
11close-twice: exit(0)
12EOF
13(close-twice) begin
14(close-twice) open "sample.txt"
15(close-twice) close "sample.txt"
16(close-twice) close "sample.txt" again
17close-twice: exit(-1)
18EOF
19pass;
diff --git a/pintos-progos/tests/userprog/create-bad-ptr.c b/pintos-progos/tests/userprog/create-bad-ptr.c
new file mode 100644
index 0000000..4a07bb3
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-bad-ptr.c
@@ -0,0 +1,12 @@
1/* Passes a bad pointer to the create system call,
2 which must cause the process to be terminated with exit code
3 -1. */
4
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 msg ("create(0x20101234): %d", create ((char *) 0x20101234, 0));
12}
diff --git a/pintos-progos/tests/userprog/create-bad-ptr.ck b/pintos-progos/tests/userprog/create-bad-ptr.ck
new file mode 100644
index 0000000..ac13405
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-bad-ptr.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-bad-ptr) begin
7create-bad-ptr: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/create-bound.c b/pintos-progos/tests/userprog/create-bound.c
new file mode 100644
index 0000000..0a829f3
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-bound.c
@@ -0,0 +1,14 @@
1/* Opens a file whose name spans the boundary between two pages.
2 This is valid, so it must succeed. */
3
4#include <syscall.h>
5#include "tests/userprog/boundary.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 msg ("create(\"quux.dat\"): %d",
13 create (copy_string_across_boundary ("quux.dat"), 0));
14}
diff --git a/pintos-progos/tests/userprog/create-bound.ck b/pintos-progos/tests/userprog/create-bound.ck
new file mode 100644
index 0000000..7656b7f
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-bound.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-bound) begin
7(create-bound) create("quux.dat"): 1
8(create-bound) end
9create-bound: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/create-empty.c b/pintos-progos/tests/userprog/create-empty.c
new file mode 100644
index 0000000..fa26b43
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-empty.c
@@ -0,0 +1,10 @@
1/* Tries to create a file with the empty string as its name. */
2
3#include "tests/lib.h"
4#include "tests/main.h"
5
6void
7test_main (void)
8{
9 msg ("create(\"\"): %d", create ("", 0));
10}
diff --git a/pintos-progos/tests/userprog/create-empty.ck b/pintos-progos/tests/userprog/create-empty.ck
new file mode 100644
index 0000000..93a1058
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-empty.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(create-empty) begin
7(create-empty) create(""): 0
8(create-empty) end
9create-empty: exit(0)
10EOF
11(create-empty) begin
12create-empty: exit(-1)
13EOF
14pass;
diff --git a/pintos-progos/tests/userprog/create-exists.c b/pintos-progos/tests/userprog/create-exists.c
new file mode 100644
index 0000000..d395008
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-exists.c
@@ -0,0 +1,16 @@
1/* Verifies that trying to create a file under a name that
2 already exists will fail. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 CHECK (create ("quux.dat", 0), "create quux.dat");
12 CHECK (create ("warble.dat", 0), "create warble.dat");
13 CHECK (!create ("quux.dat", 0), "try to re-create quux.dat");
14 CHECK (create ("baffle.dat", 0), "create baffle.dat");
15 CHECK (!create ("warble.dat", 0), "try to re-create quux.dat");
16}
diff --git a/pintos-progos/tests/userprog/create-exists.ck b/pintos-progos/tests/userprog/create-exists.ck
new file mode 100644
index 0000000..006885e
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-exists.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-exists) begin
7(create-exists) create quux.dat
8(create-exists) create warble.dat
9(create-exists) try to re-create quux.dat
10(create-exists) create baffle.dat
11(create-exists) try to re-create quux.dat
12(create-exists) end
13create-exists: exit(0)
14EOF
15pass;
diff --git a/pintos-progos/tests/userprog/create-long.c b/pintos-progos/tests/userprog/create-long.c
new file mode 100644
index 0000000..16b31bd
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-long.c
@@ -0,0 +1,17 @@
1/* Tries to create a file with a name that is much too long,
2 which must fail. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 static char name[512];
13 memset (name, 'x', sizeof name);
14 name[sizeof name - 1] = '\0';
15
16 msg ("create(\"x...\"): %d", create (name, 0));
17}
diff --git a/pintos-progos/tests/userprog/create-long.ck b/pintos-progos/tests/userprog/create-long.ck
new file mode 100644
index 0000000..628411c
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-long.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-long) begin
7(create-long) create("x..."): 0
8(create-long) end
9create-long: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/create-normal.c b/pintos-progos/tests/userprog/create-normal.c
new file mode 100644
index 0000000..3cbc463
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-normal.c
@@ -0,0 +1,10 @@
1/* Creates an ordinary empty file. */
2
3#include "tests/lib.h"
4#include "tests/main.h"
5
6void
7test_main (void)
8{
9 CHECK (create ("quux.dat", 0), "create quux.dat");
10}
diff --git a/pintos-progos/tests/userprog/create-normal.ck b/pintos-progos/tests/userprog/create-normal.ck
new file mode 100644
index 0000000..ca74a6e
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-normal.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-normal) begin
7(create-normal) create quux.dat
8(create-normal) end
9create-normal: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/create-null.c b/pintos-progos/tests/userprog/create-null.c
new file mode 100644
index 0000000..287cb23
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-null.c
@@ -0,0 +1,11 @@
1/* Tries to create a file with the null pointer as its name.
2 The process must be terminated with exit code -1. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("create(NULL): %d", create (NULL, 0));
11}
diff --git a/pintos-progos/tests/userprog/create-null.ck b/pintos-progos/tests/userprog/create-null.ck
new file mode 100644
index 0000000..09b7872
--- /dev/null
+++ b/pintos-progos/tests/userprog/create-null.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(create-null) begin
7create-null: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/exec-arg.c b/pintos-progos/tests/userprog/exec-arg.c
new file mode 100644
index 0000000..82d0744
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-arg.c
@@ -0,0 +1,10 @@
1/* Tests argument passing to child processes. */
2
3#include <syscall.h>
4#include "tests/main.h"
5
6void
7test_main (void)
8{
9 wait (exec ("child-args childarg"));
10}
diff --git a/pintos-progos/tests/userprog/exec-arg.ck b/pintos-progos/tests/userprog/exec-arg.ck
new file mode 100644
index 0000000..b7533ed
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-arg.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(exec-arg) begin
7(args) begin
8(args) argc = 2
9(args) argv[0] = 'child-args'
10(args) argv[1] = 'childarg'
11(args) argv[2] = null
12(args) end
13child-args: exit(0)
14(exec-arg) end
15exec-arg: exit(0)
16EOF
17pass;
diff --git a/pintos-progos/tests/userprog/exec-bad-ptr.c b/pintos-progos/tests/userprog/exec-bad-ptr.c
new file mode 100644
index 0000000..0abadd3
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-bad-ptr.c
@@ -0,0 +1,11 @@
1/* Passes an invalid pointer to the exec system call.
2 The process must be terminated with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 exec ((char *) 0x20101234);
11}
diff --git a/pintos-progos/tests/userprog/exec-bad-ptr.ck b/pintos-progos/tests/userprog/exec-bad-ptr.ck
new file mode 100644
index 0000000..63f5f78
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-bad-ptr.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(exec-bad-ptr) begin
7(exec-bad-ptr) end
8exec-bad-ptr: exit(0)
9EOF
10(exec-bad-ptr) begin
11exec-bad-ptr: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/exec-missing.c b/pintos-progos/tests/userprog/exec-missing.c
new file mode 100644
index 0000000..bf08cad
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-missing.c
@@ -0,0 +1,12 @@
1/* Tries to execute a nonexistent process.
2 The exec system call must return -1. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 msg ("exec(\"no-such-file\"): %d", exec ("no-such-file"));
12}
diff --git a/pintos-progos/tests/userprog/exec-missing.ck b/pintos-progos/tests/userprog/exec-missing.ck
new file mode 100644
index 0000000..0ef7aaa
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-missing.ck
@@ -0,0 +1,31 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF', <<'EOF', <<'EOF']);
6(exec-missing) begin
7load: no-such-file: open failed
8(exec-missing) exec("no-such-file"): -1
9(exec-missing) end
10exec-missing: exit(0)
11EOF
12(exec-missing) begin
13(exec-missing) exec("no-such-file"): -1
14(exec-missing) end
15exec-missing: exit(0)
16EOF
17(exec-missing) begin
18load: no-such-file: open failed
19no-such-file: exit(-1)
20(exec-missing) exec("no-such-file"): -1
21(exec-missing) end
22exec-missing: exit(0)
23EOF
24(exec-missing) begin
25load: no-such-file: open failed
26(exec-missing) exec("no-such-file"): -1
27no-such-file: exit(-1)
28(exec-missing) end
29exec-missing: exit(0)
30EOF
31pass;
diff --git a/pintos-progos/tests/userprog/exec-multiple.c b/pintos-progos/tests/userprog/exec-multiple.c
new file mode 100644
index 0000000..ba4c26e
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-multiple.c
@@ -0,0 +1,14 @@
1/* Executes and waits for multiple child processes. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 wait (exec ("child-simple"));
11 wait (exec ("child-simple"));
12 wait (exec ("child-simple"));
13 wait (exec ("child-simple"));
14}
diff --git a/pintos-progos/tests/userprog/exec-multiple.ck b/pintos-progos/tests/userprog/exec-multiple.ck
new file mode 100644
index 0000000..99624cd
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-multiple.ck
@@ -0,0 +1,18 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(exec-multiple) begin
7(child-simple) run
8child-simple: exit(81)
9(child-simple) run
10child-simple: exit(81)
11(child-simple) run
12child-simple: exit(81)
13(child-simple) run
14child-simple: exit(81)
15(exec-multiple) end
16exec-multiple: exit(0)
17EOF
18pass;
diff --git a/pintos-progos/tests/userprog/exec-once.c b/pintos-progos/tests/userprog/exec-once.c
new file mode 100644
index 0000000..7bae5a1
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-once.c
@@ -0,0 +1,11 @@
1/* Executes and waits for a single child process. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 wait (exec ("child-simple"));
11}
diff --git a/pintos-progos/tests/userprog/exec-once.ck b/pintos-progos/tests/userprog/exec-once.ck
new file mode 100644
index 0000000..00b59ed
--- /dev/null
+++ b/pintos-progos/tests/userprog/exec-once.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(exec-once) begin
7(child-simple) run
8child-simple: exit(81)
9(exec-once) end
10exec-once: exit(0)
11EOF
12pass;
diff --git a/pintos-progos/tests/userprog/exit.c b/pintos-progos/tests/userprog/exit.c
new file mode 100644
index 0000000..cb4eb8f
--- /dev/null
+++ b/pintos-progos/tests/userprog/exit.c
@@ -0,0 +1,11 @@
1/* Tests the exit system call. */
2
3#include "tests/lib.h"
4#include "tests/main.h"
5
6void
7test_main (void)
8{
9 exit (57);
10 fail ("should have called exit(57)");
11}
diff --git a/pintos-progos/tests/userprog/exit.ck b/pintos-progos/tests/userprog/exit.ck
new file mode 100644
index 0000000..a552702
--- /dev/null
+++ b/pintos-progos/tests/userprog/exit.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(exit) begin
7exit: exit(57)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/halt.c b/pintos-progos/tests/userprog/halt.c
new file mode 100644
index 0000000..4a99bce
--- /dev/null
+++ b/pintos-progos/tests/userprog/halt.c
@@ -0,0 +1,11 @@
1/* Tests the halt system call. */
2
3#include "tests/lib.h"
4#include "tests/main.h"
5
6void
7test_main (void)
8{
9 halt ();
10 fail ("should have halted");
11}
diff --git a/pintos-progos/tests/userprog/halt.ck b/pintos-progos/tests/userprog/halt.ck
new file mode 100644
index 0000000..1b701ed
--- /dev/null
+++ b/pintos-progos/tests/userprog/halt.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5
6our ($test);
7my (@output) = read_text_file ("$test.output");
8
9common_checks ("run", @output);
10
11fail "missing 'begin' message\n"
12 if !grep ($_ eq '(halt) begin', @output);
13fail "found 'fail' message--halt didn't really halt\n"
14 if grep ($_ eq '(halt) fail', @output);
15pass;
diff --git a/pintos-progos/tests/userprog/lib/.gitignore b/pintos-progos/tests/userprog/lib/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/pintos-progos/tests/userprog/lib/.gitignore
@@ -0,0 +1 @@
*.d
diff --git a/pintos-progos/tests/userprog/lib/user/.dummy b/pintos-progos/tests/userprog/lib/user/.dummy
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/pintos-progos/tests/userprog/lib/user/.dummy
diff --git a/pintos-progos/tests/userprog/lib/user/.gitignore b/pintos-progos/tests/userprog/lib/user/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/pintos-progos/tests/userprog/lib/user/.gitignore
@@ -0,0 +1 @@
*.d
diff --git a/pintos-progos/tests/userprog/multi-child-fd.c b/pintos-progos/tests/userprog/multi-child-fd.c
new file mode 100644
index 0000000..48de4b4
--- /dev/null
+++ b/pintos-progos/tests/userprog/multi-child-fd.c
@@ -0,0 +1,25 @@
1/* Opens a file and then runs a subprocess that tries to close
2 the file. (Pintos does not have inheritance of file handles,
3 so this must fail.) The parent process then attempts to use
4 the file handle, which must succeed. */
5
6#include <stdio.h>
7#include <syscall.h>
8#include "tests/userprog/sample.inc"
9#include "tests/lib.h"
10#include "tests/main.h"
11
12void
13test_main (void)
14{
15 char child_cmd[128];
16 int handle;
17
18 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
19
20 snprintf (child_cmd, sizeof child_cmd, "child-close %d", handle);
21
22 msg ("wait(exec()) = %d", wait (exec (child_cmd)));
23
24 check_file_handle (handle, "sample.txt", sample, sizeof sample - 1);
25}
diff --git a/pintos-progos/tests/userprog/multi-child-fd.ck b/pintos-progos/tests/userprog/multi-child-fd.ck
new file mode 100644
index 0000000..d0b3a33
--- /dev/null
+++ b/pintos-progos/tests/userprog/multi-child-fd.ck
@@ -0,0 +1,25 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(multi-child-fd) begin
7(multi-child-fd) open "sample.txt"
8(child-close) begin
9(child-close) end
10child-close: exit(0)
11(multi-child-fd) wait(exec()) = 0
12(multi-child-fd) verified contents of "sample.txt"
13(multi-child-fd) end
14multi-child-fd: exit(0)
15EOF
16(multi-child-fd) begin
17(multi-child-fd) open "sample.txt"
18(child-close) begin
19child-close: exit(-1)
20(multi-child-fd) wait(exec()) = -1
21(multi-child-fd) verified contents of "sample.txt"
22(multi-child-fd) end
23multi-child-fd: exit(0)
24EOF
25pass;
diff --git a/pintos-progos/tests/userprog/multi-recurse.c b/pintos-progos/tests/userprog/multi-recurse.c
new file mode 100644
index 0000000..7172ec3
--- /dev/null
+++ b/pintos-progos/tests/userprog/multi-recurse.c
@@ -0,0 +1,34 @@
1/* Executes itself recursively to the depth indicated by the
2 first command-line argument. */
3
4#include <debug.h>
5#include <stdlib.h>
6#include <stdio.h>
7#include <syscall.h>
8#include "tests/lib.h"
9
10const char *test_name = "multi-recurse";
11
12int
13main (int argc UNUSED, char *argv[])
14{
15 int n = atoi (argv[1]);
16
17 msg ("begin %d", n);
18 if (n != 0)
19 {
20 char child_cmd[128];
21 pid_t child_pid;
22 int code;
23
24 snprintf (child_cmd, sizeof child_cmd, "multi-recurse %d", n - 1);
25 CHECK ((child_pid = exec (child_cmd)) != -1, "exec(\"%s\")", child_cmd);
26
27 code = wait (child_pid);
28 if (code != n - 1)
29 fail ("wait(exec(\"%s\")) returned %d", child_cmd, code);
30 }
31
32 msg ("end %d", n);
33 return n;
34}
diff --git a/pintos-progos/tests/userprog/multi-recurse.ck b/pintos-progos/tests/userprog/multi-recurse.ck
new file mode 100644
index 0000000..41eb4a6
--- /dev/null
+++ b/pintos-progos/tests/userprog/multi-recurse.ck
@@ -0,0 +1,70 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(multi-recurse) begin 15
7(multi-recurse) exec("multi-recurse 14")
8(multi-recurse) begin 14
9(multi-recurse) exec("multi-recurse 13")
10(multi-recurse) begin 13
11(multi-recurse) exec("multi-recurse 12")
12(multi-recurse) begin 12
13(multi-recurse) exec("multi-recurse 11")
14(multi-recurse) begin 11
15(multi-recurse) exec("multi-recurse 10")
16(multi-recurse) begin 10
17(multi-recurse) exec("multi-recurse 9")
18(multi-recurse) begin 9
19(multi-recurse) exec("multi-recurse 8")
20(multi-recurse) begin 8
21(multi-recurse) exec("multi-recurse 7")
22(multi-recurse) begin 7
23(multi-recurse) exec("multi-recurse 6")
24(multi-recurse) begin 6
25(multi-recurse) exec("multi-recurse 5")
26(multi-recurse) begin 5
27(multi-recurse) exec("multi-recurse 4")
28(multi-recurse) begin 4
29(multi-recurse) exec("multi-recurse 3")
30(multi-recurse) begin 3
31(multi-recurse) exec("multi-recurse 2")
32(multi-recurse) begin 2
33(multi-recurse) exec("multi-recurse 1")
34(multi-recurse) begin 1
35(multi-recurse) exec("multi-recurse 0")
36(multi-recurse) begin 0
37(multi-recurse) end 0
38multi-recurse: exit(0)
39(multi-recurse) end 1
40multi-recurse: exit(1)
41(multi-recurse) end 2
42multi-recurse: exit(2)
43(multi-recurse) end 3
44multi-recurse: exit(3)
45(multi-recurse) end 4
46multi-recurse: exit(4)
47(multi-recurse) end 5
48multi-recurse: exit(5)
49(multi-recurse) end 6
50multi-recurse: exit(6)
51(multi-recurse) end 7
52multi-recurse: exit(7)
53(multi-recurse) end 8
54multi-recurse: exit(8)
55(multi-recurse) end 9
56multi-recurse: exit(9)
57(multi-recurse) end 10
58multi-recurse: exit(10)
59(multi-recurse) end 11
60multi-recurse: exit(11)
61(multi-recurse) end 12
62multi-recurse: exit(12)
63(multi-recurse) end 13
64multi-recurse: exit(13)
65(multi-recurse) end 14
66multi-recurse: exit(14)
67(multi-recurse) end 15
68multi-recurse: exit(15)
69EOF
70pass;
diff --git a/pintos-progos/tests/userprog/no-vm/Make.tests b/pintos-progos/tests/userprog/no-vm/Make.tests
new file mode 100644
index 0000000..a545e18
--- /dev/null
+++ b/pintos-progos/tests/userprog/no-vm/Make.tests
@@ -0,0 +1,8 @@
1# -*- makefile -*-
2
3tests/userprog/no-vm_TESTS = tests/userprog/no-vm/multi-oom
4tests/userprog/no-vm_PROGS = $(tests/userprog/no-vm_TESTS)
5tests/userprog/no-vm/multi-oom_SRC = tests/userprog/no-vm/multi-oom.c \
6tests/lib.c
7
8tests/userprog/no-vm/multi-oom.output: TIMEOUT = 360
diff --git a/pintos-progos/tests/userprog/no-vm/Rubric b/pintos-progos/tests/userprog/no-vm/Rubric
new file mode 100644
index 0000000..c3816c6
--- /dev/null
+++ b/pintos-progos/tests/userprog/no-vm/Rubric
@@ -0,0 +1,3 @@
1Functionality of features that VM might break:
2
31 multi-oom
diff --git a/pintos-progos/tests/userprog/no-vm/multi-oom.c b/pintos-progos/tests/userprog/no-vm/multi-oom.c
new file mode 100644
index 0000000..6a4472d
--- /dev/null
+++ b/pintos-progos/tests/userprog/no-vm/multi-oom.c
@@ -0,0 +1,179 @@
1/* Recursively executes itself until the child fails to execute.
2 We expect that at least 30 copies can run.
3
4 We count how many children your kernel was able to execute
5 before it fails to start a new process. We require that,
6 if a process doesn't actually get to start, exec() must
7 return -1, not a valid PID.
8
9 We repeat this process 10 times, checking that your kernel
10 allows for the same level of depth every time.
11
12 In addition, some processes will spawn children that terminate
13 abnormally after allocating some resources.
14
15 Written by Godmar Back <godmar@gmail.com>
16 */
17
18#include <debug.h>
19#include <stdio.h>
20#include <string.h>
21#include <stdlib.h>
22#include <stdbool.h>
23#include <syscall.h>
24#include <random.h>
25#include "tests/lib.h"
26
27static const int EXPECTED_DEPTH_TO_PASS = 30;
28static const int EXPECTED_REPETITIONS = 10;
29
30const char *test_name = "multi-oom";
31
32enum child_termination_mode { RECURSE, CRASH };
33
34/* Spawn a recursive copy of ourselves, passing along instructions
35 for the child. */
36static pid_t
37spawn_child (int c, enum child_termination_mode mode)
38{
39 char child_cmd[128];
40 snprintf (child_cmd, sizeof child_cmd,
41 "%s %d %s", test_name, c, mode == CRASH ? "-k" : "");
42 return exec (child_cmd);
43}
44
45/* Open a number of files (and fail to close them).
46 The kernel must free any kernel resources associated
47 with these file descriptors. */
48static void
49consume_some_resources (void)
50{
51 int fd, fdmax = 126;
52
53 /* Open as many files as we can, up to fdmax.
54 Depending on how file descriptors are allocated inside
55 the kernel, open() may fail if the kernel is low on memory.
56 A low-memory condition in open() should not lead to the
57 termination of the process. */
58 for (fd = 0; fd < fdmax; fd++)
59 if (open (test_name) == -1)
60 break;
61}
62
63/* Consume some resources, then terminate this process
64 in some abnormal way. */
65static int NO_INLINE
66consume_some_resources_and_die (int seed)
67{
68 consume_some_resources ();
69 random_init (seed);
70 int *PHYS_BASE = (int *)0xC0000000;
71
72 switch (random_ulong () % 5)
73 {
74 case 0:
75 *(int *) NULL = 42;
76
77 case 1:
78 return *(int *) NULL;
79
80 case 2:
81 return *PHYS_BASE;
82
83 case 3:
84 *PHYS_BASE = 42;
85
86 case 4:
87 open ((char *)PHYS_BASE);
88 exit (-1);
89
90 default:
91 NOT_REACHED ();
92 }
93 return 0;
94}
95
96/* The first copy is invoked without command line arguments.
97 Subsequent copies are invoked with a parameter 'depth'
98 that describes how many parent processes preceded them.
99 Each process spawns one or multiple recursive copies of
100 itself, passing 'depth+1' as depth.
101
102 Some children are started with the '-k' flag, which will
103 result in abnormal termination.
104 */
105int
106main (int argc, char *argv[])
107{
108 int n;
109
110 n = argc > 1 ? atoi (argv[1]) : 0;
111 bool is_at_root = (n == 0);
112 if (is_at_root)
113 msg ("begin");
114
115 /* If -k is passed, crash this process. */
116 if (argc > 2 && !strcmp(argv[2], "-k"))
117 {
118 consume_some_resources_and_die (n);
119 NOT_REACHED ();
120 }
121
122 int howmany = is_at_root ? EXPECTED_REPETITIONS : 1;
123 int i, expected_depth = -1;
124
125 for (i = 0; i < howmany; i++)
126 {
127 pid_t child_pid;
128
129 /* Spawn a child that will be abnormally terminated.
130 To speed the test up, do this only for processes
131 spawned at a certain depth. */
132 if (n > EXPECTED_DEPTH_TO_PASS/2)
133 {
134 child_pid = spawn_child (n + 1, CRASH);
135 if (child_pid != -1)
136 {
137 if (wait (child_pid) != -1)
138 fail ("crashed child should return -1.");
139 }
140 /* If spawning this child failed, so should
141 the next spawn_child below. */
142 }
143
144 /* Now spawn the child that will recurse. */
145 child_pid = spawn_child (n + 1, RECURSE);
146
147 /* If maximum depth is reached, return result. */
148 if (child_pid == -1)
149 return n;
150
151 /* Else wait for child to report how deeply it was able to recurse. */
152 int reached_depth = wait (child_pid);
153 if (reached_depth == -1)
154 fail ("wait returned -1.");
155
156 /* Record the depth reached during the first run; on subsequent
157 runs, fail if those runs do not match the depth achieved on the
158 first run. */
159 if (i == 0)
160 expected_depth = reached_depth;
161 else if (expected_depth != reached_depth)
162 fail ("after run %d/%d, expected depth %d, actual depth %d.",
163 i, howmany, expected_depth, reached_depth);
164 ASSERT (expected_depth == reached_depth);
165 }
166
167 consume_some_resources ();
168
169 if (n == 0)
170 {
171 if (expected_depth < EXPECTED_DEPTH_TO_PASS)
172 fail ("should have forked at least %d times.", EXPECTED_DEPTH_TO_PASS);
173 msg ("success. program forked %d times.", howmany);
174 msg ("end");
175 }
176
177 return expected_depth;
178}
179// vim: sw=2
diff --git a/pintos-progos/tests/userprog/no-vm/multi-oom.ck b/pintos-progos/tests/userprog/no-vm/multi-oom.ck
new file mode 100644
index 0000000..59a0bcd
--- /dev/null
+++ b/pintos-progos/tests/userprog/no-vm/multi-oom.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(multi-oom) begin
7(multi-oom) success. program forked 10 times.
8(multi-oom) end
9EOF
10pass;
diff --git a/pintos-progos/tests/userprog/null.ck b/pintos-progos/tests/userprog/null.ck
new file mode 100644
index 0000000..980de35
--- /dev/null
+++ b/pintos-progos/tests/userprog/null.ck
@@ -0,0 +1,8 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6system call!
7EOF
8pass;
diff --git a/pintos-progos/tests/userprog/open-bad-ptr.c b/pintos-progos/tests/userprog/open-bad-ptr.c
new file mode 100644
index 0000000..9cd4edf
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-bad-ptr.c
@@ -0,0 +1,13 @@
1/* Passes an invalid pointer to the open system call.
2 The process must be terminated with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 msg ("open(0x20101234): %d", open ((char *) 0x20101234));
12 fail ("should have called exit(-1)");
13}
diff --git a/pintos-progos/tests/userprog/open-bad-ptr.ck b/pintos-progos/tests/userprog/open-bad-ptr.ck
new file mode 100644
index 0000000..45349e2
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-bad-ptr.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(open-bad-ptr) begin
7(open-bad-ptr) end
8open-bad-ptr: exit(0)
9EOF
10(open-bad-ptr) begin
11open-bad-ptr: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/open-boundary.c b/pintos-progos/tests/userprog/open-boundary.c
new file mode 100644
index 0000000..cc8ff8b
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-boundary.c
@@ -0,0 +1,14 @@
1/* Creates a file whose name spans the boundary between two pages.
2 This is valid, so it must succeed. */
3
4#include <syscall.h>
5#include "tests/userprog/boundary.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 CHECK (open (copy_string_across_boundary ("sample.txt")) > 1,
13 "open \"sample.txt\"");
14}
diff --git a/pintos-progos/tests/userprog/open-boundary.ck b/pintos-progos/tests/userprog/open-boundary.ck
new file mode 100644
index 0000000..8060d22
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-boundary.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(open-boundary) begin
7(open-boundary) open "sample.txt"
8(open-boundary) end
9open-boundary: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/open-empty.c b/pintos-progos/tests/userprog/open-empty.c
new file mode 100644
index 0000000..3ea9907
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-empty.c
@@ -0,0 +1,13 @@
1/* Tries to open a file with the empty string as its name. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle = open ("");
11 if (handle != -1)
12 fail ("open() returned %d instead of -1", handle);
13}
diff --git a/pintos-progos/tests/userprog/open-empty.ck b/pintos-progos/tests/userprog/open-empty.ck
new file mode 100644
index 0000000..885fb41
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-empty.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(open-empty) begin
7(open-empty) end
8open-empty: exit(0)
9EOF
10pass;
diff --git a/pintos-progos/tests/userprog/open-missing.c b/pintos-progos/tests/userprog/open-missing.c
new file mode 100644
index 0000000..13ecbda
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-missing.c
@@ -0,0 +1,13 @@
1/* Tries to open a nonexistent file. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle = open ("no-such-file");
11 if (handle != -1)
12 fail ("open() returned %d", handle);
13}
diff --git a/pintos-progos/tests/userprog/open-missing.ck b/pintos-progos/tests/userprog/open-missing.ck
new file mode 100644
index 0000000..d72d878
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-missing.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(open-missing) begin
7(open-missing) end
8open-missing: exit(0)
9EOF
10pass;
diff --git a/pintos-progos/tests/userprog/open-normal.c b/pintos-progos/tests/userprog/open-normal.c
new file mode 100644
index 0000000..5132465
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-normal.c
@@ -0,0 +1,13 @@
1/* Open a file. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle = open ("sample.txt");
11 if (handle < 2)
12 fail ("open() returned %d", handle);
13}
diff --git a/pintos-progos/tests/userprog/open-normal.ck b/pintos-progos/tests/userprog/open-normal.ck
new file mode 100644
index 0000000..4f6c342
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-normal.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(open-normal) begin
7(open-normal) end
8open-normal: exit(0)
9EOF
10pass;
diff --git a/pintos-progos/tests/userprog/open-null.c b/pintos-progos/tests/userprog/open-null.c
new file mode 100644
index 0000000..bb418b8
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-null.c
@@ -0,0 +1,12 @@
1/* Tries to open a file with the null pointer as its name.
2 The process must be terminated with exit code -1. */
3
4#include <stddef.h>
5#include <syscall.h>
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 open (NULL);
12}
diff --git a/pintos-progos/tests/userprog/open-null.ck b/pintos-progos/tests/userprog/open-null.ck
new file mode 100644
index 0000000..b4a3bcb
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-null.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(open-null) begin
7(open-null) end
8open-null: exit(0)
9EOF
10(open-null) begin
11open-null: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/open-twice.c b/pintos-progos/tests/userprog/open-twice.c
new file mode 100644
index 0000000..dd333af
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-twice.c
@@ -0,0 +1,19 @@
1/* Tries to open the same file twice,
2 which must succeed and must return a different file descriptor
3 in each case. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 int h1 = open ("sample.txt");
13 int h2 = open ("sample.txt");
14
15 CHECK ((h1 = open ("sample.txt")) > 1, "open \"sample.txt\" once");
16 CHECK ((h2 = open ("sample.txt")) > 1, "open \"sample.txt\" again");
17 if (h1 == h2)
18 fail ("open() returned %d both times", h1);
19}
diff --git a/pintos-progos/tests/userprog/open-twice.ck b/pintos-progos/tests/userprog/open-twice.ck
new file mode 100644
index 0000000..64fa805
--- /dev/null
+++ b/pintos-progos/tests/userprog/open-twice.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(open-twice) begin
7(open-twice) open "sample.txt" once
8(open-twice) open "sample.txt" again
9(open-twice) end
10open-twice: exit(0)
11EOF
12pass;
diff --git a/pintos-progos/tests/userprog/read-bad-fd.c b/pintos-progos/tests/userprog/read-bad-fd.c
new file mode 100644
index 0000000..a8b190d
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-bad-fd.c
@@ -0,0 +1,21 @@
1/* Tries to read from an invalid fd,
2 which must either fail silently or terminate the process with
3 exit code -1. */
4
5#include <limits.h>
6#include <syscall.h>
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char buf;
14 read (0x20101234, &buf, 1);
15 read (5, &buf, 1);
16 read (1234, &buf, 1);
17 read (-1, &buf, 1);
18 read (-1024, &buf, 1);
19 read (INT_MIN, &buf, 1);
20 read (INT_MAX, &buf, 1);
21}
diff --git a/pintos-progos/tests/userprog/read-bad-fd.ck b/pintos-progos/tests/userprog/read-bad-fd.ck
new file mode 100644
index 0000000..5fedcc7
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-bad-fd.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(read-bad-fd) begin
7(read-bad-fd) end
8read-bad-fd: exit(0)
9EOF
10(read-bad-fd) begin
11read-bad-fd: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/read-bad-ptr.c b/pintos-progos/tests/userprog/read-bad-ptr.c
new file mode 100644
index 0000000..8fe756e
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-bad-ptr.c
@@ -0,0 +1,16 @@
1/* Passes an invalid pointer to the read system call.
2 The process must be terminated with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle;
12 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
13
14 read (handle, (char *) 0xc0100000, 123);
15 fail ("should not have survived read()");
16}
diff --git a/pintos-progos/tests/userprog/read-bad-ptr.ck b/pintos-progos/tests/userprog/read-bad-ptr.ck
new file mode 100644
index 0000000..d10accf
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-bad-ptr.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(read-bad-ptr) begin
7(read-bad-ptr) open "sample.txt"
8(read-bad-ptr) end
9read-bad-ptr: exit(0)
10EOF
11(read-bad-ptr) begin
12(read-bad-ptr) open "sample.txt"
13read-bad-ptr: exit(-1)
14EOF
15pass;
diff --git a/pintos-progos/tests/userprog/read-boundary.c b/pintos-progos/tests/userprog/read-boundary.c
new file mode 100644
index 0000000..9c19966
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-boundary.c
@@ -0,0 +1,30 @@
1/* Reads data spanning two pages in virtual address space,
2 which must succeed. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/userprog/boundary.h"
7#include "tests/userprog/sample.inc"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11void
12test_main (void)
13{
14 int handle;
15 int byte_cnt;
16 char *buffer;
17
18 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
19
20 buffer = get_boundary_area () - sizeof sample / 2;
21 byte_cnt = read (handle, buffer, sizeof sample - 1);
22 if (byte_cnt != sizeof sample - 1)
23 fail ("read() returned %d instead of %zu", byte_cnt, sizeof sample - 1);
24 else if (strcmp (sample, buffer))
25 {
26 msg ("expected text:\n%s", sample);
27 msg ("text actually read:\n%s", buffer);
28 fail ("expected text differs from actual");
29 }
30}
diff --git a/pintos-progos/tests/userprog/read-boundary.ck b/pintos-progos/tests/userprog/read-boundary.ck
new file mode 100644
index 0000000..08dc161
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-boundary.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(read-boundary) begin
7(read-boundary) open "sample.txt"
8(read-boundary) end
9read-boundary: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/read-normal.c b/pintos-progos/tests/userprog/read-normal.c
new file mode 100644
index 0000000..16d15cc
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-normal.c
@@ -0,0 +1,11 @@
1/* Try reading a file in the most normal way. */
2
3#include "tests/userprog/sample.inc"
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 check_file ("sample.txt", sample, sizeof sample - 1);
11}
diff --git a/pintos-progos/tests/userprog/read-normal.ck b/pintos-progos/tests/userprog/read-normal.ck
new file mode 100644
index 0000000..0ed2998
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-normal.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(read-normal) begin
7(read-normal) open "sample.txt" for verification
8(read-normal) verified contents of "sample.txt"
9(read-normal) close "sample.txt"
10(read-normal) end
11read-normal: exit(0)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/read-stdout.c b/pintos-progos/tests/userprog/read-stdout.c
new file mode 100644
index 0000000..d0630b9
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-stdout.c
@@ -0,0 +1,14 @@
1/* Try reading from fd 1 (stdout),
2 which may just fail or terminate the process with -1 exit
3 code. */
4
5#include <stdio.h>
6#include <syscall.h>
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 char buf;
13 read (STDOUT_FILENO, &buf, 1);
14}
diff --git a/pintos-progos/tests/userprog/read-stdout.ck b/pintos-progos/tests/userprog/read-stdout.ck
new file mode 100644
index 0000000..7d87b52
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-stdout.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(read-stdout) begin
7(read-stdout) end
8read-stdout: exit(0)
9EOF
10(read-stdout) begin
11read-stdout: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/read-zero.c b/pintos-progos/tests/userprog/read-zero.c
new file mode 100644
index 0000000..e441817
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-zero.c
@@ -0,0 +1,22 @@
1/* Try a 0-byte read, which should return 0 without reading
2 anything. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle, byte_cnt;
12 char buf;
13
14 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
15
16 buf = 123;
17 byte_cnt = read (handle, &buf, 0);
18 if (byte_cnt != 0)
19 fail ("read() returned %d instead of 0", byte_cnt);
20 else if (buf != 123)
21 fail ("0-byte read() modified buffer");
22}
diff --git a/pintos-progos/tests/userprog/read-zero.ck b/pintos-progos/tests/userprog/read-zero.ck
new file mode 100644
index 0000000..8346dbc
--- /dev/null
+++ b/pintos-progos/tests/userprog/read-zero.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(read-zero) begin
7(read-zero) open "sample.txt"
8(read-zero) end
9read-zero: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/rox-child.c b/pintos-progos/tests/userprog/rox-child.c
new file mode 100644
index 0000000..30afba2
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-child.c
@@ -0,0 +1,5 @@
1/* Ensure that the executable of a running process cannot be
2 modified, even by a child process. */
3
4#define CHILD_CNT "1"
5#include "tests/userprog/rox-child.inc"
diff --git a/pintos-progos/tests/userprog/rox-child.ck b/pintos-progos/tests/userprog/rox-child.ck
new file mode 100644
index 0000000..e6363fb
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-child.ck
@@ -0,0 +1,20 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(rox-child) begin
7(rox-child) open "child-rox"
8(rox-child) read "child-rox"
9(rox-child) write "child-rox"
10(rox-child) exec "child-rox 1"
11(child-rox) begin
12(child-rox) try to write "child-rox"
13(child-rox) try to write "child-rox"
14(child-rox) end
15child-rox: exit(12)
16(rox-child) write "child-rox"
17(rox-child) end
18rox-child: exit(0)
19EOF
20pass;
diff --git a/pintos-progos/tests/userprog/rox-child.inc b/pintos-progos/tests/userprog/rox-child.inc
new file mode 100644
index 0000000..1e2ade9
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-child.inc
@@ -0,0 +1,33 @@
1/* -*- c -*- */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 const char *child_cmd = "child-rox " CHILD_CNT;
11 int handle;
12 pid_t child;
13 char buffer[16];
14
15 /* Open child-rox, read from it, write back same data. */
16 CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\"");
17 CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer,
18 "read \"child-rox\"");
19 seek (handle, 0);
20 CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer,
21 "write \"child-rox\"");
22
23 /* Execute child-rox and wait for it. */
24 CHECK ((child = exec (child_cmd)) != -1, "exec \"%s\"", child_cmd);
25 quiet = true;
26 CHECK (wait (child) == 12, "wait for child");
27 quiet = false;
28
29 /* Write to child-rox again. */
30 seek (handle, 0);
31 CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer,
32 "write \"child-rox\"");
33}
diff --git a/pintos-progos/tests/userprog/rox-multichild.c b/pintos-progos/tests/userprog/rox-multichild.c
new file mode 100644
index 0000000..8e74dab
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-multichild.c
@@ -0,0 +1,5 @@
1/* Ensure that the executable of a running process cannot be
2 modified, even in the presence of multiple children. */
3
4#define CHILD_CNT "5"
5#include "tests/userprog/rox-child.inc"
diff --git a/pintos-progos/tests/userprog/rox-multichild.ck b/pintos-progos/tests/userprog/rox-multichild.ck
new file mode 100644
index 0000000..14b27db
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-multichild.ck
@@ -0,0 +1,44 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(rox-multichild) begin
7(rox-multichild) open "child-rox"
8(rox-multichild) read "child-rox"
9(rox-multichild) write "child-rox"
10(rox-multichild) exec "child-rox 5"
11(child-rox) begin
12(child-rox) try to write "child-rox"
13(child-rox) exec "child-rox 4"
14(child-rox) begin
15(child-rox) try to write "child-rox"
16(child-rox) exec "child-rox 3"
17(child-rox) begin
18(child-rox) try to write "child-rox"
19(child-rox) exec "child-rox 2"
20(child-rox) begin
21(child-rox) try to write "child-rox"
22(child-rox) exec "child-rox 1"
23(child-rox) begin
24(child-rox) try to write "child-rox"
25(child-rox) try to write "child-rox"
26(child-rox) end
27child-rox: exit(12)
28(child-rox) try to write "child-rox"
29(child-rox) end
30child-rox: exit(12)
31(child-rox) try to write "child-rox"
32(child-rox) end
33child-rox: exit(12)
34(child-rox) try to write "child-rox"
35(child-rox) end
36child-rox: exit(12)
37(child-rox) try to write "child-rox"
38(child-rox) end
39child-rox: exit(12)
40(rox-multichild) write "child-rox"
41(rox-multichild) end
42rox-multichild: exit(0)
43EOF
44pass;
diff --git a/pintos-progos/tests/userprog/rox-simple.c b/pintos-progos/tests/userprog/rox-simple.c
new file mode 100644
index 0000000..e84a064
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-simple.c
@@ -0,0 +1,19 @@
1/* Ensure that the executable of a running process cannot be
2 modified. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle;
12 char buffer[16];
13
14 CHECK ((handle = open ("rox-simple")) > 1, "open \"rox-simple\"");
15 CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer,
16 "read \"rox-simple\"");
17 CHECK (write (handle, buffer, sizeof buffer) == 0,
18 "try to write \"rox-simple\"");
19}
diff --git a/pintos-progos/tests/userprog/rox-simple.ck b/pintos-progos/tests/userprog/rox-simple.ck
new file mode 100644
index 0000000..c9dcc66
--- /dev/null
+++ b/pintos-progos/tests/userprog/rox-simple.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(rox-simple) begin
7(rox-simple) open "rox-simple"
8(rox-simple) read "rox-simple"
9(rox-simple) try to write "rox-simple"
10(rox-simple) end
11rox-simple: exit(0)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/sample.inc b/pintos-progos/tests/userprog/sample.inc
new file mode 100644
index 0000000..59f2bcb
--- /dev/null
+++ b/pintos-progos/tests/userprog/sample.inc
@@ -0,0 +1,6 @@
1char sample[] = {
2 "\"Amazing Electronic Fact: If you scuffed your feet long enough without\n"
3 " touching anything, you would build up so many electrons that your\n"
4 " finger would explode! But this is nothing to worry about unless you\n"
5 " have carpeting.\" --Dave Barry\n"
6};
diff --git a/pintos-progos/tests/userprog/sample.txt b/pintos-progos/tests/userprog/sample.txt
new file mode 100644
index 0000000..5050fec
--- /dev/null
+++ b/pintos-progos/tests/userprog/sample.txt
@@ -0,0 +1,4 @@
1"Amazing Electronic Fact: If you scuffed your feet long enough without
2 touching anything, you would build up so many electrons that your
3 finger would explode! But this is nothing to worry about unless you
4 have carpeting." --Dave Barry
diff --git a/pintos-progos/tests/userprog/sc-bad-arg.c b/pintos-progos/tests/userprog/sc-bad-arg.c
new file mode 100644
index 0000000..0b512a0
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-bad-arg.c
@@ -0,0 +1,17 @@
1/* Sticks a system call number (SYS_EXIT) at the very top of the
2 stack, then invokes a system call with the stack pointer
3 (%esp) set to its address. The process must be terminated
4 with -1 exit code because the argument to the system call
5 would be above the top of the user address space. */
6
7#include <syscall-nr.h>
8#include "tests/lib.h"
9#include "tests/main.h"
10
11void
12test_main (void)
13{
14 asm volatile ("movl $0xbffffffc, %%esp; movl %0, (%%esp); int $0x30"
15 : : "i" (SYS_EXIT));
16 fail ("should have called exit(-1)");
17}
diff --git a/pintos-progos/tests/userprog/sc-bad-arg.ck b/pintos-progos/tests/userprog/sc-bad-arg.ck
new file mode 100644
index 0000000..8981105
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-bad-arg.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(sc-bad-arg) begin
7sc-bad-arg: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/sc-bad-sp.c b/pintos-progos/tests/userprog/sc-bad-sp.c
new file mode 100644
index 0000000..39cce84
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-bad-sp.c
@@ -0,0 +1,20 @@
1/* Invokes a system call with the stack pointer (%esp) set to a
2 bad address. The process must be terminated with -1 exit
3 code.
4
5 For Project 3: The bad address lies approximately 64MB below
6 the code segment, so there is no ambiguity that this attempt
7 must be rejected even after stack growth is implemented.
8 Moreover, a good stack growth heuristics should probably not
9 grow the stack for the purpose of reading the system call
10 number and arguments. */
11
12#include "tests/lib.h"
13#include "tests/main.h"
14
15void
16test_main (void)
17{
18 asm volatile ("movl $.-(64*1024*1024), %esp; int $0x30");
19 fail ("should have called exit(-1)");
20}
diff --git a/pintos-progos/tests/userprog/sc-bad-sp.ck b/pintos-progos/tests/userprog/sc-bad-sp.ck
new file mode 100644
index 0000000..498cec1
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-bad-sp.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(sc-bad-sp) begin
7sc-bad-sp: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/sc-boundary-2.c b/pintos-progos/tests/userprog/sc-boundary-2.c
new file mode 100644
index 0000000..8acf036
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-boundary-2.c
@@ -0,0 +1,22 @@
1/* Invokes a system call with one byte of the system call's
2 argument on a separate page from the rest of the bytes. This
3 must work. */
4
5#include <syscall-nr.h>
6#include "tests/userprog/boundary.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 /* Make one byte of a syscall argument hang over into a second
14 page. */
15 int *p = (int *) ((char *) get_boundary_area () - 7);
16 p[0] = SYS_EXIT;
17 p[1] = 67;
18
19 /* Invoke the system call. */
20 asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p));
21 fail ("should have called exit(67)");
22}
diff --git a/pintos-progos/tests/userprog/sc-boundary-2.ck b/pintos-progos/tests/userprog/sc-boundary-2.ck
new file mode 100644
index 0000000..43766bf
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-boundary-2.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(sc-boundary-2) begin
7sc-boundary-2: exit(67)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/sc-boundary.c b/pintos-progos/tests/userprog/sc-boundary.c
new file mode 100644
index 0000000..d889535
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-boundary.c
@@ -0,0 +1,22 @@
1/* Invokes a system call with the system call number and its
2 argument on separate pages. This must work. */
3
4#include <syscall-nr.h>
5#include "tests/userprog/boundary.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 /* Put a syscall number at the end of one page
13 and its argument at the beginning of another. */
14 int *p = get_boundary_area ();
15 p--;
16 p[0] = SYS_EXIT;
17 p[1] = 42;
18
19 /* Invoke the system call. */
20 asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p));
21 fail ("should have called exit(42)");
22}
diff --git a/pintos-progos/tests/userprog/sc-boundary.ck b/pintos-progos/tests/userprog/sc-boundary.ck
new file mode 100644
index 0000000..3f7cbaf
--- /dev/null
+++ b/pintos-progos/tests/userprog/sc-boundary.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(sc-boundary) begin
7sc-boundary: exit(42)
8EOF
9pass;
diff --git a/pintos-progos/tests/userprog/wait-bad-pid.c b/pintos-progos/tests/userprog/wait-bad-pid.c
new file mode 100644
index 0000000..3fe8ee4
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-bad-pid.c
@@ -0,0 +1,11 @@
1/* Waits for an invalid pid. This may fail or terminate the
2 process with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 wait ((pid_t) 0x0c020301);
11}
diff --git a/pintos-progos/tests/userprog/wait-bad-pid.ck b/pintos-progos/tests/userprog/wait-bad-pid.ck
new file mode 100644
index 0000000..db63fb9
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-bad-pid.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(wait-bad-pid) begin
7(wait-bad-pid) end
8wait-bad-pid: exit(0)
9EOF
10(wait-bad-pid) begin
11wait-bad-pid: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/wait-killed.c b/pintos-progos/tests/userprog/wait-killed.c
new file mode 100644
index 0000000..6a2a6b5
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-killed.c
@@ -0,0 +1,11 @@
1/* Wait for a process that will be killed for bad behavior. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("wait(exec()) = %d", wait (exec ("child-bad")));
11}
diff --git a/pintos-progos/tests/userprog/wait-killed.ck b/pintos-progos/tests/userprog/wait-killed.ck
new file mode 100644
index 0000000..5df0e9c
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-killed.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(wait-killed) begin
7(child-bad) begin
8child-bad: exit(-1)
9(wait-killed) wait(exec()) = -1
10(wait-killed) end
11wait-killed: exit(0)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/wait-simple.c b/pintos-progos/tests/userprog/wait-simple.c
new file mode 100644
index 0000000..d3afcf3
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-simple.c
@@ -0,0 +1,11 @@
1/* Wait for a subprocess to finish. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 msg ("wait(exec()) = %d", wait (exec ("child-simple")));
11}
diff --git a/pintos-progos/tests/userprog/wait-simple.ck b/pintos-progos/tests/userprog/wait-simple.ck
new file mode 100644
index 0000000..93dd577
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-simple.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(wait-simple) begin
7(child-simple) run
8child-simple: exit(81)
9(wait-simple) wait(exec()) = 81
10(wait-simple) end
11wait-simple: exit(0)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/wait-twice.c b/pintos-progos/tests/userprog/wait-twice.c
new file mode 100644
index 0000000..785e684
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-twice.c
@@ -0,0 +1,15 @@
1/* Wait for a subprocess to finish, twice.
2 The first call must wait in the usual way and return the exit code.
3 The second wait call must return -1 immediately. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 pid_t child = exec ("child-simple");
13 msg ("wait(exec()) = %d", wait (child));
14 msg ("wait(exec()) = %d", wait (child));
15}
diff --git a/pintos-progos/tests/userprog/wait-twice.ck b/pintos-progos/tests/userprog/wait-twice.ck
new file mode 100644
index 0000000..6d53843
--- /dev/null
+++ b/pintos-progos/tests/userprog/wait-twice.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(wait-twice) begin
7(child-simple) run
8child-simple: exit(81)
9(wait-twice) wait(exec()) = 81
10(wait-twice) wait(exec()) = -1
11(wait-twice) end
12wait-twice: exit(0)
13EOF
14pass;
diff --git a/pintos-progos/tests/userprog/write-bad-fd.c b/pintos-progos/tests/userprog/write-bad-fd.c
new file mode 100644
index 0000000..f3b1151
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-bad-fd.c
@@ -0,0 +1,20 @@
1/* Tries to write to an invalid fd,
2 which must either fail silently or terminate the process with
3 exit code -1. */
4
5#include <limits.h>
6#include <syscall.h>
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 char buf = 123;
13 write (0x01012342, &buf, 1);
14 write (7, &buf, 1);
15 write (2546, &buf, 1);
16 write (-5, &buf, 1);
17 write (-8192, &buf, 1);
18 write (INT_MIN + 1, &buf, 1);
19 write (INT_MAX - 1, &buf, 1);
20}
diff --git a/pintos-progos/tests/userprog/write-bad-fd.ck b/pintos-progos/tests/userprog/write-bad-fd.ck
new file mode 100644
index 0000000..8da7a8b
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-bad-fd.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(write-bad-fd) begin
7(write-bad-fd) end
8write-bad-fd: exit(0)
9EOF
10(write-bad-fd) begin
11write-bad-fd: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/write-bad-ptr.c b/pintos-progos/tests/userprog/write-bad-ptr.c
new file mode 100644
index 0000000..5336479
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-bad-ptr.c
@@ -0,0 +1,16 @@
1/* Passes an invalid pointer to the write system call.
2 The process must be terminated with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle;
12 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
13
14 write (handle, (char *) 0x10123420, 123);
15 fail ("should have exited with -1");
16}
diff --git a/pintos-progos/tests/userprog/write-bad-ptr.ck b/pintos-progos/tests/userprog/write-bad-ptr.ck
new file mode 100644
index 0000000..ad9f399
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-bad-ptr.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(write-bad-ptr) begin
7(write-bad-ptr) open "sample.txt"
8(write-bad-ptr) end
9write-bad-ptr: exit(0)
10EOF
11(write-bad-ptr) begin
12(write-bad-ptr) open "sample.txt"
13write-bad-ptr: exit(-1)
14EOF
15pass;
diff --git a/pintos-progos/tests/userprog/write-boundary.c b/pintos-progos/tests/userprog/write-boundary.c
new file mode 100644
index 0000000..d2de1d4
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-boundary.c
@@ -0,0 +1,25 @@
1/* Writes data spanning two pages in virtual address space,
2 which must succeed. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/userprog/boundary.h"
7#include "tests/userprog/sample.inc"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11void
12test_main (void)
13{
14 int handle;
15 int byte_cnt;
16 char *sample_p;
17
18 sample_p = copy_string_across_boundary (sample);
19
20 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
21
22 byte_cnt = write (handle, sample_p, sizeof sample - 1);
23 if (byte_cnt != sizeof sample - 1)
24 fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1);
25}
diff --git a/pintos-progos/tests/userprog/write-boundary.ck b/pintos-progos/tests/userprog/write-boundary.ck
new file mode 100644
index 0000000..7883781
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-boundary.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(write-boundary) begin
7(write-boundary) open "sample.txt"
8(write-boundary) end
9write-boundary: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/userprog/write-normal.c b/pintos-progos/tests/userprog/write-normal.c
new file mode 100644
index 0000000..e0297aa
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-normal.c
@@ -0,0 +1,20 @@
1/* Try writing a file in the most normal way. */
2
3#include <syscall.h>
4#include "tests/userprog/sample.inc"
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle, byte_cnt;
12
13 CHECK (create ("test.txt", sizeof sample - 1), "create \"test.txt\"");
14 CHECK ((handle = open ("test.txt")) > 1, "open \"test.txt\"");
15
16 byte_cnt = write (handle, sample, sizeof sample - 1);
17 if (byte_cnt != sizeof sample - 1)
18 fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1);
19}
20
diff --git a/pintos-progos/tests/userprog/write-normal.ck b/pintos-progos/tests/userprog/write-normal.ck
new file mode 100644
index 0000000..9fa6024
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-normal.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(write-normal) begin
7(write-normal) create "test.txt"
8(write-normal) open "test.txt"
9(write-normal) end
10write-normal: exit(0)
11EOF
12pass;
diff --git a/pintos-progos/tests/userprog/write-stdin.c b/pintos-progos/tests/userprog/write-stdin.c
new file mode 100644
index 0000000..491ea53
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-stdin.c
@@ -0,0 +1,14 @@
1/* Try writing to fd 0 (stdin),
2 which may just fail or terminate the process with -1 exit
3 code. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 char buf = 123;
13 write (0, &buf, 1);
14}
diff --git a/pintos-progos/tests/userprog/write-stdin.ck b/pintos-progos/tests/userprog/write-stdin.ck
new file mode 100644
index 0000000..a6caf81
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-stdin.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(write-stdin) begin
7(write-stdin) end
8write-stdin: exit(0)
9EOF
10(write-stdin) begin
11write-stdin: exit(-1)
12EOF
13pass;
diff --git a/pintos-progos/tests/userprog/write-zero.c b/pintos-progos/tests/userprog/write-zero.c
new file mode 100644
index 0000000..d8dac9b
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-zero.c
@@ -0,0 +1,20 @@
1/* Try a 0-byte write, which should return 0 without writing
2 anything. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle, byte_cnt;
12 char buf;
13
14 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
15
16 buf = 123;
17 byte_cnt = write (handle, &buf, 0);
18 if (byte_cnt != 0)
19 fail("write() returned %d instead of 0", byte_cnt);
20}
diff --git a/pintos-progos/tests/userprog/write-zero.ck b/pintos-progos/tests/userprog/write-zero.ck
new file mode 100644
index 0000000..cc4cd60
--- /dev/null
+++ b/pintos-progos/tests/userprog/write-zero.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(write-zero) begin
7(write-zero) open "sample.txt"
8(write-zero) end
9write-zero: exit(0)
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/Grading b/pintos-progos/tests/vm/Grading
new file mode 100644
index 0000000..f0c2c13
--- /dev/null
+++ b/pintos-progos/tests/vm/Grading
@@ -0,0 +1,12 @@
1# Percentage of the testing point total designated for each set of
2# tests.
3
4# This project is primarily about virtual memory, but all the previous
5# functionality should work too, and it's easy to screw it up, thus
6# the equal weight placed on each.
7
850% tests/vm/Rubric.functionality
915% tests/vm/Rubric.robustness
1010% tests/userprog/Rubric.functionality
115% tests/userprog/Rubric.robustness
1220% tests/filesys/base/Rubric
diff --git a/pintos-progos/tests/vm/Make.tests b/pintos-progos/tests/vm/Make.tests
new file mode 100644
index 0000000..0307ccc
--- /dev/null
+++ b/pintos-progos/tests/vm/Make.tests
@@ -0,0 +1,107 @@
1# -*- makefile -*-
2
3tests/vm_TESTS = $(addprefix tests/vm/,pt-grow-stack pt-grow-pusha \
4pt-grow-bad pt-big-stk-obj pt-bad-addr pt-bad-read pt-write-code \
5pt-write-code2 pt-grow-stk-sc mmap-read \
6mmap-close mmap-unmap mmap-overlap mmap-twice mmap-write mmap-exit \
7mmap-shuffle mmap-bad-fd mmap-clean mmap-inherit mmap-misalign \
8mmap-null mmap-over-code mmap-over-data mmap-over-stk mmap-remove \
9mmap-zero mmap-lazy-seq)
10
11# Deactivated
12PAGE_TESTS=page-linear page-parallel page-merge-seq \
13page-merge-par page-merge-stk page-merge-mm page-shuffle
14
15tests/vm_PROGS = $(tests/vm_TESTS) $(addprefix tests/vm/,child-linear \
16child-sort child-qsort child-qsort-mm child-mm-wrt child-inherit)
17
18tests/vm/pt-grow-stack_SRC = tests/vm/pt-grow-stack.c tests/arc4.c \
19tests/cksum.c tests/lib.c tests/main.c
20tests/vm/pt-grow-pusha_SRC = tests/vm/pt-grow-pusha.c tests/lib.c \
21tests/main.c
22tests/vm/pt-grow-bad_SRC = tests/vm/pt-grow-bad.c tests/lib.c tests/main.c
23tests/vm/pt-big-stk-obj_SRC = tests/vm/pt-big-stk-obj.c tests/arc4.c \
24tests/cksum.c tests/lib.c tests/main.c
25tests/vm/pt-bad-addr_SRC = tests/vm/pt-bad-addr.c tests/lib.c tests/main.c
26tests/vm/pt-bad-read_SRC = tests/vm/pt-bad-read.c tests/lib.c tests/main.c
27tests/vm/pt-write-code_SRC = tests/vm/pt-write-code.c tests/lib.c tests/main.c
28tests/vm/pt-write-code2_SRC = tests/vm/pt-write-code-2.c tests/lib.c tests/main.c
29tests/vm/pt-grow-stk-sc_SRC = tests/vm/pt-grow-stk-sc.c tests/lib.c tests/main.c
30tests/vm/page-linear_SRC = tests/vm/page-linear.c tests/arc4.c \
31tests/lib.c tests/main.c
32tests/vm/page-parallel_SRC = tests/vm/page-parallel.c tests/lib.c tests/main.c
33tests/vm/page-merge-seq_SRC = tests/vm/page-merge-seq.c tests/arc4.c \
34tests/lib.c tests/main.c
35tests/vm/page-merge-par_SRC = tests/vm/page-merge-par.c \
36tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
37tests/vm/page-merge-stk_SRC = tests/vm/page-merge-stk.c \
38tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
39tests/vm/page-merge-mm_SRC = tests/vm/page-merge-mm.c \
40tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
41tests/vm/page-shuffle_SRC = tests/vm/page-shuffle.c tests/arc4.c \
42tests/cksum.c tests/lib.c tests/main.c
43tests/vm/mmap-read_SRC = tests/vm/mmap-read.c tests/lib.c tests/main.c
44tests/vm/mmap-close_SRC = tests/vm/mmap-close.c tests/lib.c tests/main.c
45tests/vm/mmap-unmap_SRC = tests/vm/mmap-unmap.c tests/lib.c tests/main.c
46tests/vm/mmap-overlap_SRC = tests/vm/mmap-overlap.c tests/lib.c tests/main.c
47tests/vm/mmap-twice_SRC = tests/vm/mmap-twice.c tests/lib.c tests/main.c
48tests/vm/mmap-write_SRC = tests/vm/mmap-write.c tests/lib.c tests/main.c
49tests/vm/mmap-lazy-seq_SRC = tests/vm/mmap-lazy-seq.c tests/lib.c tests/main.c
50tests/vm/mmap-exit_SRC = tests/vm/mmap-exit.c tests/lib.c tests/main.c
51tests/vm/mmap-shuffle_SRC = tests/vm/mmap-shuffle.c tests/arc4.c \
52tests/cksum.c tests/lib.c tests/main.c
53tests/vm/mmap-bad-fd_SRC = tests/vm/mmap-bad-fd.c tests/lib.c tests/main.c
54tests/vm/mmap-clean_SRC = tests/vm/mmap-clean.c tests/lib.c tests/main.c
55tests/vm/mmap-inherit_SRC = tests/vm/mmap-inherit.c tests/lib.c tests/main.c
56tests/vm/mmap-misalign_SRC = tests/vm/mmap-misalign.c tests/lib.c \
57tests/main.c
58tests/vm/mmap-null_SRC = tests/vm/mmap-null.c tests/lib.c tests/main.c
59tests/vm/mmap-over-code_SRC = tests/vm/mmap-over-code.c tests/lib.c \
60tests/main.c
61tests/vm/mmap-over-data_SRC = tests/vm/mmap-over-data.c tests/lib.c \
62tests/main.c
63tests/vm/mmap-over-stk_SRC = tests/vm/mmap-over-stk.c tests/lib.c tests/main.c
64tests/vm/mmap-remove_SRC = tests/vm/mmap-remove.c tests/lib.c tests/main.c
65tests/vm/mmap-zero_SRC = tests/vm/mmap-zero.c tests/lib.c tests/main.c
66
67tests/vm/child-linear_SRC = tests/vm/child-linear.c tests/arc4.c tests/lib.c
68tests/vm/child-qsort_SRC = tests/vm/child-qsort.c tests/vm/qsort.c tests/lib.c
69tests/vm/child-qsort-mm_SRC = tests/vm/child-qsort-mm.c tests/vm/qsort.c \
70tests/lib.c
71tests/vm/child-sort_SRC = tests/vm/child-sort.c tests/lib.c
72tests/vm/child-mm-wrt_SRC = tests/vm/child-mm-wrt.c tests/lib.c tests/main.c
73tests/vm/child-inherit_SRC = tests/vm/child-inherit.c tests/lib.c tests/main.c
74
75tests/vm/pt-bad-read_PUTFILES = tests/vm/sample.txt
76tests/vm/pt-write-code2_PUTFILES = tests/vm/sample.txt
77tests/vm/mmap-close_PUTFILES = tests/vm/sample.txt
78tests/vm/mmap-read_PUTFILES = tests/vm/sample.txt
79tests/vm/mmap-unmap_PUTFILES = tests/vm/sample.txt
80tests/vm/mmap-twice_PUTFILES = tests/vm/sample.txt
81tests/vm/mmap-overlap_PUTFILES = tests/vm/zeros
82tests/vm/mmap-exit_PUTFILES = tests/vm/child-mm-wrt
83tests/vm/page-parallel_PUTFILES = tests/vm/child-linear
84tests/vm/page-merge-seq_PUTFILES = tests/vm/child-sort
85tests/vm/page-merge-par_PUTFILES = tests/vm/child-sort
86tests/vm/page-merge-stk_PUTFILES = tests/vm/child-qsort
87tests/vm/page-merge-mm_PUTFILES = tests/vm/child-qsort-mm
88tests/vm/mmap-clean_PUTFILES = tests/vm/sample.txt
89tests/vm/mmap-inherit_PUTFILES = tests/vm/sample.txt tests/vm/child-inherit
90tests/vm/mmap-misalign_PUTFILES = tests/vm/sample.txt
91tests/vm/mmap-null_PUTFILES = tests/vm/sample.txt
92tests/vm/mmap-over-code_PUTFILES = tests/vm/sample.txt
93tests/vm/mmap-over-data_PUTFILES = tests/vm/sample.txt
94tests/vm/mmap-over-stk_PUTFILES = tests/vm/sample.txt
95tests/vm/mmap-remove_PUTFILES = tests/vm/sample.txt
96
97tests/vm/page-linear.output: TIMEOUT = 300
98tests/vm/page-shuffle.output: TIMEOUT = 600
99tests/vm/mmap-shuffle.output: TIMEOUT = 600
100tests/vm/page-merge-seq.output: TIMEOUT = 600
101tests/vm/page-merge-par.output: TIMEOUT = 600
102
103tests/vm/zeros:
104 dd if=/dev/zero of=$@ bs=1024 count=6
105
106clean::
107 rm -f tests/vm/zeros
diff --git a/pintos-progos/tests/vm/Rubric.functionality b/pintos-progos/tests/vm/Rubric.functionality
new file mode 100644
index 0000000..ede0221
--- /dev/null
+++ b/pintos-progos/tests/vm/Rubric.functionality
@@ -0,0 +1,21 @@
1Functionality of virtual memory subsystem:
2- Test stack growth.
33 pt-grow-stack
43 pt-grow-stk-sc
53 pt-big-stk-obj
63 pt-grow-pusha
7
8- Test "mmap" system call.
92 mmap-read
102 mmap-write
112 mmap-shuffle
12
132 mmap-twice
14
152 mmap-unmap
161 mmap-exit
17
183 mmap-clean
19
202 mmap-close
212 mmap-remove
diff --git a/pintos-progos/tests/vm/Rubric.paging b/pintos-progos/tests/vm/Rubric.paging
new file mode 100644
index 0000000..cf1d871
--- /dev/null
+++ b/pintos-progos/tests/vm/Rubric.paging
@@ -0,0 +1,8 @@
1- Test paging behavior.
23 page-linear
33 page-parallel
43 page-shuffle
54 page-merge-seq
64 page-merge-par
74 page-merge-mm
84 page-merge-stk
diff --git a/pintos-progos/tests/vm/Rubric.robustness b/pintos-progos/tests/vm/Rubric.robustness
new file mode 100644
index 0000000..eca0af4
--- /dev/null
+++ b/pintos-progos/tests/vm/Rubric.robustness
@@ -0,0 +1,21 @@
1Robustness of virtual memory subsystem:
2- Test robustness of page table support.
32 pt-bad-addr
43 pt-bad-read
52 pt-write-code
63 pt-write-code2
74 pt-grow-bad
8
9- Test robustness of "mmap" system call.
101 mmap-bad-fd
111 mmap-inherit
121 mmap-null
131 mmap-zero
14
152 mmap-misalign
16
172 mmap-over-code
182 mmap-over-data
192 mmap-over-stk
202 mmap-overlap
214 mmap-lazy-seq
diff --git a/pintos-progos/tests/vm/child-inherit.c b/pintos-progos/tests/vm/child-inherit.c
new file mode 100644
index 0000000..d3186a1
--- /dev/null
+++ b/pintos-progos/tests/vm/child-inherit.c
@@ -0,0 +1,16 @@
1/* Child process for mmap-inherit test.
2 Tries to write to a mapping present in the parent.
3 The process must be terminated with -1 exit code. */
4
5#include <string.h>
6#include "tests/vm/sample.inc"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 memset ((char *) 0x54321000, 0, 4096);
14 fail ("child can modify parent's memory mappings");
15}
16
diff --git a/pintos-progos/tests/vm/child-linear.c b/pintos-progos/tests/vm/child-linear.c
new file mode 100644
index 0000000..eca3e3f
--- /dev/null
+++ b/pintos-progos/tests/vm/child-linear.c
@@ -0,0 +1,36 @@
1/* Child process of page-parallel.
2 Encrypts 1 MB of zeros, then decrypts it, and ensures that
3 the zeros are back. */
4
5#include <string.h>
6#include "tests/arc4.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10const char *test_name = "child-linear";
11
12#define SIZE (1024 * 1024)
13static char buf[SIZE];
14
15int
16main (int argc, char *argv[])
17{
18 const char *key = argv[argc - 1];
19 struct arc4 arc4;
20 size_t i;
21
22 /* Encrypt zeros. */
23 arc4_init (&arc4, key, strlen (key));
24 arc4_crypt (&arc4, buf, SIZE);
25
26 /* Decrypt back to zeros. */
27 arc4_init (&arc4, key, strlen (key));
28 arc4_crypt (&arc4, buf, SIZE);
29
30 /* Check that it's all zeros. */
31 for (i = 0; i < SIZE; i++)
32 if (buf[i] != '\0')
33 fail ("byte %zu != 0", i);
34
35 return 0x42;
36}
diff --git a/pintos-progos/tests/vm/child-mm-wrt.c b/pintos-progos/tests/vm/child-mm-wrt.c
new file mode 100644
index 0000000..8419788
--- /dev/null
+++ b/pintos-progos/tests/vm/child-mm-wrt.c
@@ -0,0 +1,24 @@
1/* Child process of mmap-exit.
2 Mmaps a file and writes to it via the mmap'ing, then exits
3 without calling munmap. The data in the mapped region must be
4 written out at program termination. */
5
6#include <string.h>
7#include <syscall.h>
8#include "tests/vm/sample.inc"
9#include "tests/lib.h"
10#include "tests/main.h"
11
12#define ACTUAL ((void *) 0x10000000)
13
14void
15test_main (void)
16{
17 int handle;
18
19 CHECK (create ("sample.txt", sizeof sample), "create \"sample.txt\"");
20 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
21 CHECK (mmap (handle, ACTUAL) != MAP_FAILED, "mmap \"sample.txt\"");
22 memcpy (ACTUAL, sample, sizeof sample);
23}
24
diff --git a/pintos-progos/tests/vm/child-qsort-mm.c b/pintos-progos/tests/vm/child-qsort-mm.c
new file mode 100644
index 0000000..db45499
--- /dev/null
+++ b/pintos-progos/tests/vm/child-qsort-mm.c
@@ -0,0 +1,25 @@
1/* Mmaps a 128 kB file "sorts" the bytes in it, using quick sort,
2 a multi-pass divide and conquer algorithm. */
3
4#include <debug.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8#include "tests/vm/qsort.h"
9
10const char *test_name = "child-qsort-mm";
11
12int
13main (int argc UNUSED, char *argv[])
14{
15 int handle;
16 unsigned char *p = (unsigned char *) 0x10000000;
17
18 quiet = true;
19
20 CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
21 CHECK (mmap (handle, p) != MAP_FAILED, "mmap \"%s\"", argv[1]);
22 qsort_bytes (p, 1024 * 128);
23
24 return 80;
25}
diff --git a/pintos-progos/tests/vm/child-qsort.c b/pintos-progos/tests/vm/child-qsort.c
new file mode 100644
index 0000000..355f4eb
--- /dev/null
+++ b/pintos-progos/tests/vm/child-qsort.c
@@ -0,0 +1,32 @@
1/* Reads a 128 kB file onto the stack and "sorts" the bytes in
2 it, using quick sort, a multi-pass divide and conquer
3 algorithm. The sorted data is written back to the same file
4 in-place. */
5
6#include <debug.h>
7#include <syscall.h>
8#include "tests/lib.h"
9#include "tests/main.h"
10#include "tests/vm/qsort.h"
11
12const char *test_name = "child-qsort";
13
14int
15main (int argc UNUSED, char *argv[])
16{
17 int handle;
18 unsigned char buf[128 * 1024];
19 size_t size;
20
21 quiet = true;
22
23 CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
24
25 size = read (handle, buf, sizeof buf);
26 qsort_bytes (buf, sizeof buf);
27 seek (handle, 0);
28 write (handle, buf, size);
29 close (handle);
30
31 return 72;
32}
diff --git a/pintos-progos/tests/vm/child-sort.c b/pintos-progos/tests/vm/child-sort.c
new file mode 100644
index 0000000..dff2c77
--- /dev/null
+++ b/pintos-progos/tests/vm/child-sort.c
@@ -0,0 +1,42 @@
1/* Reads a 128 kB file into static data and "sorts" the bytes in
2 it, using counting sort, a single-pass algorithm. The sorted
3 data is written back to the same file in-place. */
4
5#include <debug.h>
6#include <syscall.h>
7#include "tests/lib.h"
8#include "tests/main.h"
9
10const char *test_name = "child-sort";
11
12unsigned char buf[128 * 1024];
13size_t histogram[256];
14
15int
16main (int argc UNUSED, char *argv[])
17{
18 int handle;
19 unsigned char *p;
20 size_t size;
21 size_t i;
22
23 quiet = true;
24
25 CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
26
27 size = read (handle, buf, sizeof buf);
28 for (i = 0; i < size; i++)
29 histogram[buf[i]]++;
30 p = buf;
31 for (i = 0; i < sizeof histogram / sizeof *histogram; i++)
32 {
33 size_t j = histogram[i];
34 while (j-- > 0)
35 *p++ = i;
36 }
37 seek (handle, 0);
38 write (handle, buf, size);
39 close (handle);
40
41 return 123;
42}
diff --git a/pintos-progos/tests/vm/mmap-bad-fd.c b/pintos-progos/tests/vm/mmap-bad-fd.c
new file mode 100644
index 0000000..76a7b50
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-bad-fd.c
@@ -0,0 +1,15 @@
1/* Tries to mmap an invalid fd,
2 which must either fail silently or terminate the process with
3 exit code -1. */
4
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 CHECK (mmap (0x5678, (void *) 0x10000000) == MAP_FAILED,
13 "try to mmap invalid fd");
14}
15
diff --git a/pintos-progos/tests/vm/mmap-bad-fd.ck b/pintos-progos/tests/vm/mmap-bad-fd.ck
new file mode 100644
index 0000000..f3f58d5
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-bad-fd.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF', <<'EOF']);
6(mmap-bad-fd) begin
7(mmap-bad-fd) try to mmap invalid fd
8(mmap-bad-fd) end
9mmap-bad-fd: exit(0)
10EOF
11(mmap-bad-fd) begin
12(mmap-bad-fd) try to mmap invalid fd
13mmap-bad-fd: exit(-1)
14EOF
15pass;
diff --git a/pintos-progos/tests/vm/mmap-clean.c b/pintos-progos/tests/vm/mmap-clean.c
new file mode 100644
index 0000000..ea1dc9c
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-clean.c
@@ -0,0 +1,53 @@
1/* Verifies that mmap'd regions are only written back on munmap
2 if the data was actually modified in memory. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/vm/sample.inc"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 static const char overwrite[] = "Now is the time for all good...";
14 static char buffer[sizeof sample - 1];
15 char *actual = (char *) 0x54321000;
16 int handle;
17 mapid_t map;
18
19 /* Open file, map, verify data. */
20 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
21 CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\"");
22 if (memcmp (actual, sample, strlen (sample)))
23 fail ("read of mmap'd file reported bad data");
24
25 /* Modify file. */
26 CHECK (write (handle, overwrite, strlen (overwrite))
27 == (int) strlen (overwrite),
28 "write \"sample.txt\"");
29
30 /* Close mapping. Data should not be written back, because we
31 didn't modify it via the mapping. */
32 msg ("munmap \"sample.txt\"");
33 munmap (map);
34
35 /* Read file back. */
36 msg ("seek \"sample.txt\"");
37 seek (handle, 0);
38 CHECK (read (handle, buffer, sizeof buffer) == sizeof buffer,
39 "read \"sample.txt\"");
40
41 /* Verify that file overwrite worked. */
42 if (memcmp (buffer, overwrite, strlen (overwrite))
43 || memcmp (buffer + strlen (overwrite), sample + strlen (overwrite),
44 strlen (sample) - strlen (overwrite)))
45 {
46 if (!memcmp (buffer, sample, strlen (sample)))
47 fail ("munmap wrote back clean page");
48 else
49 fail ("read surprising data from file");
50 }
51 else
52 msg ("file change was retained after munmap");
53}
diff --git a/pintos-progos/tests/vm/mmap-clean.ck b/pintos-progos/tests/vm/mmap-clean.ck
new file mode 100644
index 0000000..1666d6c
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-clean.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-clean) begin
7(mmap-clean) open "sample.txt"
8(mmap-clean) mmap "sample.txt"
9(mmap-clean) write "sample.txt"
10(mmap-clean) munmap "sample.txt"
11(mmap-clean) seek "sample.txt"
12(mmap-clean) read "sample.txt"
13(mmap-clean) file change was retained after munmap
14(mmap-clean) end
15EOF
16pass;
diff --git a/pintos-progos/tests/vm/mmap-close.c b/pintos-progos/tests/vm/mmap-close.c
new file mode 100644
index 0000000..d016ee3
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-close.c
@@ -0,0 +1,27 @@
1/* Verifies that memory mappings persist after file close. */
2
3#include <string.h>
4#include <syscall.h>
5#include "tests/vm/sample.inc"
6#include "tests/arc4.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10#define ACTUAL ((void *) 0x10000000)
11
12void
13test_main (void)
14{
15 int handle;
16 mapid_t map;
17
18 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
19 CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\"");
20
21 close (handle);
22
23 if (memcmp (ACTUAL, sample, strlen (sample)))
24 fail ("read of mmap'd file reported bad data");
25
26 munmap (map);
27}
diff --git a/pintos-progos/tests/vm/mmap-close.ck b/pintos-progos/tests/vm/mmap-close.ck
new file mode 100644
index 0000000..d15e41a
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-close.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-close) begin
7(mmap-close) open "sample.txt"
8(mmap-close) mmap "sample.txt"
9(mmap-close) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-exit.c b/pintos-progos/tests/vm/mmap-exit.c
new file mode 100644
index 0000000..7a2278a
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-exit.c
@@ -0,0 +1,22 @@
1/* Executes child-mm-wrt and verifies that the writes that should
2 have occurred really did. */
3
4#include <syscall.h>
5#include "tests/vm/sample.inc"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 pid_t child;
13
14 /* Make child write file. */
15 quiet = true;
16 CHECK ((child = exec ("child-mm-wrt")) != -1, "exec \"child-mm-wrt\"");
17 CHECK (wait (child) == 0, "wait for child (should return 0)");
18 quiet = false;
19
20 /* Check file contents. */
21 check_file ("sample.txt", sample, sizeof sample);
22}
diff --git a/pintos-progos/tests/vm/mmap-exit.ck b/pintos-progos/tests/vm/mmap-exit.ck
new file mode 100644
index 0000000..457d34a
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-exit.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-exit) begin
7(child-mm-wrt) begin
8(child-mm-wrt) create "sample.txt"
9(child-mm-wrt) open "sample.txt"
10(child-mm-wrt) mmap "sample.txt"
11(child-mm-wrt) end
12(mmap-exit) open "sample.txt" for verification
13(mmap-exit) verified contents of "sample.txt"
14(mmap-exit) close "sample.txt"
15(mmap-exit) end
16EOF
17pass;
diff --git a/pintos-progos/tests/vm/mmap-inherit.c b/pintos-progos/tests/vm/mmap-inherit.c
new file mode 100644
index 0000000..7fa9607
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-inherit.c
@@ -0,0 +1,32 @@
1/* Maps a file into memory and runs child-inherit to verify that
2 mappings are not inherited. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/vm/sample.inc"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char *actual = (char *) 0x54321000;
14 int handle;
15 pid_t child;
16
17 /* Open file, map, verify data. */
18 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
19 CHECK (mmap (handle, actual) != MAP_FAILED, "mmap \"sample.txt\"");
20 if (memcmp (actual, sample, strlen (sample)))
21 fail ("read of mmap'd file reported bad data");
22
23 /* Spawn child and wait. */
24 CHECK ((child = exec ("child-inherit")) != -1, "exec \"child-inherit\"");
25 quiet = true;
26 CHECK (wait (child) == -1, "wait for child (should return -1)");
27 quiet = false;
28
29 /* Verify data again. */
30 CHECK (!memcmp (actual, sample, strlen (sample)),
31 "checking that mmap'd file still has same data");
32}
diff --git a/pintos-progos/tests/vm/mmap-inherit.ck b/pintos-progos/tests/vm/mmap-inherit.ck
new file mode 100644
index 0000000..c54638a
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-inherit.ck
@@ -0,0 +1,16 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(mmap-inherit) begin
7(mmap-inherit) open "sample.txt"
8(mmap-inherit) mmap "sample.txt"
9(mmap-inherit) exec "child-inherit"
10(child-inherit) begin
11child-inherit: exit(-1)
12(mmap-inherit) checking that mmap'd file still has same data
13(mmap-inherit) end
14mmap-inherit: exit(0)
15EOF
16pass;
diff --git a/pintos-progos/tests/vm/mmap-lazy-seq.c b/pintos-progos/tests/vm/mmap-lazy-seq.c
new file mode 100644
index 0000000..b8f07bd
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-lazy-seq.c
@@ -0,0 +1,52 @@
1/* Create a large file, and mmap it several times, writing to
2 different pages. Then unmaps the file, and reads the data back
3 to verify */
4
5#include <string.h>
6#include <syscall.h>
7#include "tests/vm/sample.inc"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11/* Offset needs to be larger or equal to page size */
12#define OFFSET(i) (8192*(i))
13/* Number of times file is mmapped */
14#define N (8)
15/* Size of file */
16#define FILE_SIZE (1024*1024)
17/* Address for mmap */
18#define ACTUAL(i) ((void *) (0x10000000 + (i)*FILE_SIZE))
19
20
21void
22test_main (void)
23{
24 int i;
25 int handle;
26 mapid_t map[N];
27 char buf[1024];
28 /* create file */
29 CHECK (create ("sample.txt", FILE_SIZE), "create \"sample.txt\"");
30 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
31 /* mmap */
32 for (i = 0; i < N; i++) {
33 CHECK ((map[i] = mmap (handle, ACTUAL(i))) != MAP_FAILED, "mmap \"sample.txt\"");
34 }
35 /* write */
36 for (i = 0; i < N; i++) {
37 memcpy (buf, ACTUAL(i)+OFFSET(i+N), 1024); /* not checked */
38 memcpy (ACTUAL(i)+OFFSET(i), sample, strlen (sample));
39 }
40 /* munmap */
41 for (i = 0; i < N; i++) {
42 munmap (map[i]);
43 }
44 /* Read back via read(). */
45 for (i = 0; i < N; i++) {
46 seek (handle, OFFSET(i));
47 read (handle, buf, strlen (sample));
48 CHECK (!memcmp (buf, sample, strlen (sample)),
49 "compare read data against written data");
50 }
51 close (handle);
52}
diff --git a/pintos-progos/tests/vm/mmap-lazy-seq.ck b/pintos-progos/tests/vm/mmap-lazy-seq.ck
new file mode 100644
index 0000000..dd0e240
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-lazy-seq.ck
@@ -0,0 +1,27 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-lazy-seq) begin
7(mmap-lazy-seq) create "sample.txt"
8(mmap-lazy-seq) open "sample.txt"
9(mmap-lazy-seq) mmap "sample.txt"
10(mmap-lazy-seq) mmap "sample.txt"
11(mmap-lazy-seq) mmap "sample.txt"
12(mmap-lazy-seq) mmap "sample.txt"
13(mmap-lazy-seq) mmap "sample.txt"
14(mmap-lazy-seq) mmap "sample.txt"
15(mmap-lazy-seq) mmap "sample.txt"
16(mmap-lazy-seq) mmap "sample.txt"
17(mmap-lazy-seq) compare read data against written data
18(mmap-lazy-seq) compare read data against written data
19(mmap-lazy-seq) compare read data against written data
20(mmap-lazy-seq) compare read data against written data
21(mmap-lazy-seq) compare read data against written data
22(mmap-lazy-seq) compare read data against written data
23(mmap-lazy-seq) compare read data against written data
24(mmap-lazy-seq) compare read data against written data
25(mmap-lazy-seq) end
26EOF
27pass;
diff --git a/pintos-progos/tests/vm/mmap-misalign.c b/pintos-progos/tests/vm/mmap-misalign.c
new file mode 100644
index 0000000..34141a9
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-misalign.c
@@ -0,0 +1,16 @@
1/* Verifies that misaligned memory mappings are disallowed. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle;
11
12 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
13 CHECK (mmap (handle, (void *) 0x10001234) == MAP_FAILED,
14 "try to mmap at misaligned address");
15}
16
diff --git a/pintos-progos/tests/vm/mmap-misalign.ck b/pintos-progos/tests/vm/mmap-misalign.ck
new file mode 100644
index 0000000..145a2e8
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-misalign.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-misalign) begin
7(mmap-misalign) open "sample.txt"
8(mmap-misalign) try to mmap at misaligned address
9(mmap-misalign) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-null.c b/pintos-progos/tests/vm/mmap-null.c
new file mode 100644
index 0000000..f8ef075
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-null.c
@@ -0,0 +1,15 @@
1/* Verifies that memory mappings at address 0 are disallowed. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle;
11
12 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
13 CHECK (mmap (handle, NULL) == MAP_FAILED, "try to mmap at address 0");
14}
15
diff --git a/pintos-progos/tests/vm/mmap-null.ck b/pintos-progos/tests/vm/mmap-null.ck
new file mode 100644
index 0000000..aacdd65
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-null.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-null) begin
7(mmap-null) open "sample.txt"
8(mmap-null) try to mmap at address 0
9(mmap-null) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-over-code.c b/pintos-progos/tests/vm/mmap-over-code.c
new file mode 100644
index 0000000..d3619a3
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-code.c
@@ -0,0 +1,19 @@
1/* Verifies that mapping over the code segment is disallowed. */
2
3#include <stdint.h>
4#include <round.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 uintptr_t test_main_page = ROUND_DOWN ((uintptr_t) test_main, 4096);
13 int handle;
14
15 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
16 CHECK (mmap (handle, (void *) test_main_page) == MAP_FAILED,
17 "try to mmap over code segment");
18}
19
diff --git a/pintos-progos/tests/vm/mmap-over-code.ck b/pintos-progos/tests/vm/mmap-over-code.ck
new file mode 100644
index 0000000..b5b23c7
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-code.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-over-code) begin
7(mmap-over-code) open "sample.txt"
8(mmap-over-code) try to mmap over code segment
9(mmap-over-code) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-over-data.c b/pintos-progos/tests/vm/mmap-over-data.c
new file mode 100644
index 0000000..9ea5d49
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-data.c
@@ -0,0 +1,21 @@
1/* Verifies that mapping over the data segment is disallowed. */
2
3#include <stdint.h>
4#include <round.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9static char x;
10
11void
12test_main (void)
13{
14 uintptr_t x_page = ROUND_DOWN ((uintptr_t) &x, 4096);
15 int handle;
16
17 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
18 CHECK (mmap (handle, (void *) x_page) == MAP_FAILED,
19 "try to mmap over data segment");
20}
21
diff --git a/pintos-progos/tests/vm/mmap-over-data.ck b/pintos-progos/tests/vm/mmap-over-data.ck
new file mode 100644
index 0000000..98770cc
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-data.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-over-data) begin
7(mmap-over-data) open "sample.txt"
8(mmap-over-data) try to mmap over data segment
9(mmap-over-data) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-over-stk.c b/pintos-progos/tests/vm/mmap-over-stk.c
new file mode 100644
index 0000000..4e241e8
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-stk.c
@@ -0,0 +1,19 @@
1/* Verifies that mapping over the stack segment is disallowed. */
2
3#include <stdint.h>
4#include <round.h>
5#include <syscall.h>
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 int handle;
13 uintptr_t handle_page = ROUND_DOWN ((uintptr_t) &handle, 4096);
14
15 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
16 CHECK (mmap (handle, (void *) handle_page) == MAP_FAILED,
17 "try to mmap over stack segment");
18}
19
diff --git a/pintos-progos/tests/vm/mmap-over-stk.ck b/pintos-progos/tests/vm/mmap-over-stk.ck
new file mode 100644
index 0000000..e6880cf
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-over-stk.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-over-stk) begin
7(mmap-over-stk) open "sample.txt"
8(mmap-over-stk) try to mmap over stack segment
9(mmap-over-stk) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-overlap.c b/pintos-progos/tests/vm/mmap-overlap.c
new file mode 100644
index 0000000..668ae5f
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-overlap.c
@@ -0,0 +1,20 @@
1/* Verifies that overlapping memory mappings are disallowed. */
2
3#include <syscall.h>
4#include "tests/vm/sample.inc"
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 char *start = (char *) 0x10000000;
12 int fd[2];
13
14 CHECK ((fd[0] = open ("zeros")) > 1, "open \"zeros\" once");
15 CHECK (mmap (fd[0], start) != MAP_FAILED, "mmap \"zeros\"");
16 CHECK ((fd[1] = open ("zeros")) > 1 && fd[0] != fd[1],
17 "open \"zeros\" again");
18 CHECK (mmap (fd[1], start + 4096) == MAP_FAILED,
19 "try to mmap \"zeros\" again");
20}
diff --git a/pintos-progos/tests/vm/mmap-overlap.ck b/pintos-progos/tests/vm/mmap-overlap.ck
new file mode 100644
index 0000000..f13801e
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-overlap.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-overlap) begin
7(mmap-overlap) open "zeros" once
8(mmap-overlap) mmap "zeros"
9(mmap-overlap) open "zeros" again
10(mmap-overlap) try to mmap "zeros" again
11(mmap-overlap) end
12EOF
13pass;
diff --git a/pintos-progos/tests/vm/mmap-read.c b/pintos-progos/tests/vm/mmap-read.c
new file mode 100644
index 0000000..c0f23a1
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-read.c
@@ -0,0 +1,32 @@
1/* Uses a memory mapping to read a file. */
2
3#include <string.h>
4#include <syscall.h>
5#include "tests/vm/sample.inc"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9void
10test_main (void)
11{
12 char *actual = (char *) 0x10000000;
13 int handle;
14 mapid_t map;
15 size_t i;
16
17 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
18 CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\"");
19
20 /* Check that data is correct. */
21 if (memcmp (actual, sample, strlen (sample)))
22 fail ("read of mmap'd file reported bad data");
23
24 /* Verify that data is followed by zeros. */
25 for (i = strlen (sample); i < 4096; i++)
26 if (actual[i] != 0)
27 fail ("byte %zu of mmap'd region has value %02hhx (should be 0)",
28 i, actual[i]);
29
30 munmap (map);
31 close (handle);
32}
diff --git a/pintos-progos/tests/vm/mmap-read.ck b/pintos-progos/tests/vm/mmap-read.ck
new file mode 100644
index 0000000..95ab790
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-read.ck
@@ -0,0 +1,11 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-read) begin
7(mmap-read) open "sample.txt"
8(mmap-read) mmap "sample.txt"
9(mmap-read) end
10EOF
11pass;
diff --git a/pintos-progos/tests/vm/mmap-remove.c b/pintos-progos/tests/vm/mmap-remove.c
new file mode 100644
index 0000000..5f7444d
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-remove.c
@@ -0,0 +1,43 @@
1/* Deletes and closes file that is mapped into memory
2 and verifies that it can still be read through the mapping. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/vm/sample.inc"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char *actual = (char *) 0x10000000;
14 int handle;
15 mapid_t map;
16 size_t i;
17
18 /* Map file. */
19 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
20 CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\"");
21
22 /* Close file and delete it. */
23 close (handle);
24 CHECK (remove ("sample.txt"), "remove \"sample.txt\"");
25 CHECK (open ("sample.txt") == -1, "try to open \"sample.txt\"");
26
27 /* Create a new file in hopes of overwriting data from the old
28 one, in case the file system has incorrectly freed the
29 file's data. */
30 CHECK (create ("another", 4096 * 10), "create \"another\"");
31
32 /* Check that mapped data is correct. */
33 if (memcmp (actual, sample, strlen (sample)))
34 fail ("read of mmap'd file reported bad data");
35
36 /* Verify that data is followed by zeros. */
37 for (i = strlen (sample); i < 4096; i++)
38 if (actual[i] != 0)
39 fail ("byte %zu of mmap'd region has value %02hhx (should be 0)",
40 i, actual[i]);
41
42 munmap (map);
43}
diff --git a/pintos-progos/tests/vm/mmap-remove.ck b/pintos-progos/tests/vm/mmap-remove.ck
new file mode 100644
index 0000000..d3cc938
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-remove.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-remove) begin
7(mmap-remove) open "sample.txt"
8(mmap-remove) mmap "sample.txt"
9(mmap-remove) remove "sample.txt"
10(mmap-remove) try to open "sample.txt"
11(mmap-remove) create "another"
12(mmap-remove) end
13EOF
14pass;
diff --git a/pintos-progos/tests/vm/mmap-shuffle.c b/pintos-progos/tests/vm/mmap-shuffle.c
new file mode 100644
index 0000000..29921ad
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-shuffle.c
@@ -0,0 +1,38 @@
1/* Creates a 128 kB file and repeatedly shuffles data in it
2 through a memory mapping. */
3
4#include <stdio.h>
5#include <string.h>
6#include <syscall.h>
7#include "tests/arc4.h"
8#include "tests/cksum.h"
9#include "tests/lib.h"
10#include "tests/main.h"
11
12#define SIZE (128 * 1024)
13
14static char *buf = (char *) 0x10000000;
15
16void
17test_main (void)
18{
19 size_t i;
20 int handle;
21
22 /* Create file, mmap. */
23 CHECK (create ("buffer", SIZE), "create \"buffer\"");
24 CHECK ((handle = open ("buffer")) > 1, "open \"buffer\"");
25 CHECK (mmap (handle, buf) != MAP_FAILED, "mmap \"buffer\"");
26
27 /* Initialize. */
28 for (i = 0; i < SIZE; i++)
29 buf[i] = i * 257;
30 msg ("init: cksum=%lu", cksum (buf, SIZE));
31
32 /* Shuffle repeatedly. */
33 for (i = 0; i < 10; i++)
34 {
35 shuffle (buf, SIZE, 1);
36 msg ("shuffle %zu: cksum=%lu", i, cksum (buf, SIZE));
37 }
38}
diff --git a/pintos-progos/tests/vm/mmap-shuffle.ck b/pintos-progos/tests/vm/mmap-shuffle.ck
new file mode 100644
index 0000000..c158301
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-shuffle.ck
@@ -0,0 +1,47 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::cksum;
6use tests::lib;
7
8my ($init, @shuffle);
9if (1) {
10 # Use precalculated values.
11 $init = 3115322833;
12 @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467,
13 3022003332, 3614934266, 2704001777, 735775156, 1864109763);
14} else {
15 # Recalculate values.
16 my ($buf) = "";
17 for my $i (0...128 * 1024 - 1) {
18 $buf .= chr (($i * 257) & 0xff);
19 }
20 $init = cksum ($buf);
21
22 random_init (0);
23 for my $i (1...10) {
24 $buf = shuffle ($buf, length ($buf), 1);
25 push (@shuffle, cksum ($buf));
26 }
27}
28
29check_expected (IGNORE_EXIT_CODES => 1, [<<EOF]);
30(mmap-shuffle) begin
31(mmap-shuffle) create "buffer"
32(mmap-shuffle) open "buffer"
33(mmap-shuffle) mmap "buffer"
34(mmap-shuffle) init: cksum=$init
35(mmap-shuffle) shuffle 0: cksum=$shuffle[0]
36(mmap-shuffle) shuffle 1: cksum=$shuffle[1]
37(mmap-shuffle) shuffle 2: cksum=$shuffle[2]
38(mmap-shuffle) shuffle 3: cksum=$shuffle[3]
39(mmap-shuffle) shuffle 4: cksum=$shuffle[4]
40(mmap-shuffle) shuffle 5: cksum=$shuffle[5]
41(mmap-shuffle) shuffle 6: cksum=$shuffle[6]
42(mmap-shuffle) shuffle 7: cksum=$shuffle[7]
43(mmap-shuffle) shuffle 8: cksum=$shuffle[8]
44(mmap-shuffle) shuffle 9: cksum=$shuffle[9]
45(mmap-shuffle) end
46EOF
47pass;
diff --git a/pintos-progos/tests/vm/mmap-twice.c b/pintos-progos/tests/vm/mmap-twice.c
new file mode 100644
index 0000000..d277a37
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-twice.c
@@ -0,0 +1,28 @@
1/* Maps the same file into memory twice and verifies that the
2 same data is readable in both. */
3
4#include <string.h>
5#include <syscall.h>
6#include "tests/vm/sample.inc"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char *actual[2] = {(char *) 0x10000000, (char *) 0x20000000};
14 size_t i;
15 int handle[2];
16
17 for (i = 0; i < 2; i++)
18 {
19 CHECK ((handle[i] = open ("sample.txt")) > 1,
20 "open \"sample.txt\" #%zu", i);
21 CHECK (mmap (handle[i], actual[i]) != MAP_FAILED,
22 "mmap \"sample.txt\" #%zu at %p", i, (void *) actual[i]);
23 }
24
25 for (i = 0; i < 2; i++)
26 CHECK (!memcmp (actual[i], sample, strlen (sample)),
27 "compare mmap'd file %zu against data", i);
28}
diff --git a/pintos-progos/tests/vm/mmap-twice.ck b/pintos-progos/tests/vm/mmap-twice.ck
new file mode 100644
index 0000000..05e9724
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-twice.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-twice) begin
7(mmap-twice) open "sample.txt" #0
8(mmap-twice) mmap "sample.txt" #0 at 0x10000000
9(mmap-twice) open "sample.txt" #1
10(mmap-twice) mmap "sample.txt" #1 at 0x20000000
11(mmap-twice) compare mmap'd file 0 against data
12(mmap-twice) compare mmap'd file 1 against data
13(mmap-twice) end
14EOF
15pass;
diff --git a/pintos-progos/tests/vm/mmap-unmap.c b/pintos-progos/tests/vm/mmap-unmap.c
new file mode 100644
index 0000000..d35a79e
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-unmap.c
@@ -0,0 +1,23 @@
1/* Maps and unmaps a file and verifies that the mapped region is
2 inaccessible afterward. */
3
4#include <syscall.h>
5#include "tests/vm/sample.inc"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9#define ACTUAL ((void *) 0x10000000)
10
11void
12test_main (void)
13{
14 int handle;
15 mapid_t map;
16
17 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
18 CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\"");
19
20 munmap (map);
21
22 fail ("unmapped memory is readable (%d)", *(int *) ACTUAL);
23}
diff --git a/pintos-progos/tests/vm/mmap-unmap.ck b/pintos-progos/tests/vm/mmap-unmap.ck
new file mode 100644
index 0000000..119658c
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-unmap.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::vm::process_death;
6
7check_process_death ('mmap-unmap');
diff --git a/pintos-progos/tests/vm/mmap-write.c b/pintos-progos/tests/vm/mmap-write.c
new file mode 100644
index 0000000..46e8043
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-write.c
@@ -0,0 +1,32 @@
1/* Writes to a file through a mapping, and unmaps the file,
2 then reads the data in the file back using the read system
3 call to verify. */
4
5#include <string.h>
6#include <syscall.h>
7#include "tests/vm/sample.inc"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11#define ACTUAL ((void *) 0x10000000)
12
13void
14test_main (void)
15{
16 int handle;
17 mapid_t map;
18 char buf[1024];
19
20 /* Write file via mmap. */
21 CHECK (create ("sample.txt", strlen (sample)), "create \"sample.txt\"");
22 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
23 CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\"");
24 memcpy (ACTUAL, sample, strlen (sample));
25 munmap (map);
26
27 /* Read back via read(). */
28 read (handle, buf, strlen (sample));
29 CHECK (!memcmp (buf, sample, strlen (sample)),
30 "compare read data against written data");
31 close (handle);
32}
diff --git a/pintos-progos/tests/vm/mmap-write.ck b/pintos-progos/tests/vm/mmap-write.ck
new file mode 100644
index 0000000..d2c9cc5
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-write.ck
@@ -0,0 +1,13 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(mmap-write) begin
7(mmap-write) create "sample.txt"
8(mmap-write) open "sample.txt"
9(mmap-write) mmap "sample.txt"
10(mmap-write) compare read data against written data
11(mmap-write) end
12EOF
13pass;
diff --git a/pintos-progos/tests/vm/mmap-zero.c b/pintos-progos/tests/vm/mmap-zero.c
new file mode 100644
index 0000000..368b759
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-zero.c
@@ -0,0 +1,27 @@
1/* Tries to map a zero-length file, which may or may not work but
2 should not terminate the process or crash.
3 Then dereferences the address that we tried to map,
4 and the process must be terminated with -1 exit code. */
5
6#include <syscall.h>
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char *data = (char *) 0x7f000000;
14 int handle;
15
16 CHECK (create ("empty", 0), "create empty file \"empty\"");
17 CHECK ((handle = open ("empty")) > 1, "open \"empty\"");
18
19 /* Calling mmap() might succeed or fail. We don't care. */
20 msg ("mmap \"empty\"");
21 mmap (handle, data);
22
23 /* Regardless of whether the call worked, *data should cause
24 the process to be terminated. */
25 fail ("unmapped memory is readable (%d)", *data);
26}
27
diff --git a/pintos-progos/tests/vm/mmap-zero.ck b/pintos-progos/tests/vm/mmap-zero.ck
new file mode 100644
index 0000000..6033d5d
--- /dev/null
+++ b/pintos-progos/tests/vm/mmap-zero.ck
@@ -0,0 +1,12 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(mmap-zero) begin
7(mmap-zero) create empty file "empty"
8(mmap-zero) open "empty"
9(mmap-zero) mmap "empty"
10mmap-zero: exit(-1)
11EOF
12pass;
diff --git a/pintos-progos/tests/vm/page-linear.c b/pintos-progos/tests/vm/page-linear.c
new file mode 100644
index 0000000..652a47b
--- /dev/null
+++ b/pintos-progos/tests/vm/page-linear.c
@@ -0,0 +1,44 @@
1/* Encrypts, then decrypts, 2 MB of memory and verifies that the
2 values are as they should be. */
3
4#include <string.h>
5#include "tests/arc4.h"
6#include "tests/lib.h"
7#include "tests/main.h"
8
9#define SIZE (2 * 1024 * 1024)
10
11static char buf[SIZE];
12
13void
14test_main (void)
15{
16 struct arc4 arc4;
17 size_t i;
18
19 /* Initialize to 0x5a. */
20 msg ("initialize");
21 memset (buf, 0x5a, sizeof buf);
22
23 /* Check that it's all 0x5a. */
24 msg ("read pass");
25 for (i = 0; i < SIZE; i++)
26 if (buf[i] != 0x5a)
27 fail ("byte %zu != 0x5a", i);
28
29 /* Encrypt zeros. */
30 msg ("read/modify/write pass one");
31 arc4_init (&arc4, "foobar", 6);
32 arc4_crypt (&arc4, buf, SIZE);
33
34 /* Decrypt back to zeros. */
35 msg ("read/modify/write pass two");
36 arc4_init (&arc4, "foobar", 6);
37 arc4_crypt (&arc4, buf, SIZE);
38
39 /* Check that it's all 0x5a. */
40 msg ("read pass");
41 for (i = 0; i < SIZE; i++)
42 if (buf[i] != 0x5a)
43 fail ("byte %zu != 0x5a", i);
44}
diff --git a/pintos-progos/tests/vm/page-linear.ck b/pintos-progos/tests/vm/page-linear.ck
new file mode 100644
index 0000000..dcbc884
--- /dev/null
+++ b/pintos-progos/tests/vm/page-linear.ck
@@ -0,0 +1,14 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-linear) begin
7(page-linear) initialize
8(page-linear) read pass
9(page-linear) read/modify/write pass one
10(page-linear) read/modify/write pass two
11(page-linear) read pass
12(page-linear) end
13EOF
14pass;
diff --git a/pintos-progos/tests/vm/page-merge-mm.c b/pintos-progos/tests/vm/page-merge-mm.c
new file mode 100644
index 0000000..908c71c
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-mm.c
@@ -0,0 +1,8 @@
1#include "tests/main.h"
2#include "tests/vm/parallel-merge.h"
3
4void
5test_main (void)
6{
7 parallel_merge ("child-qsort-mm", 80);
8}
diff --git a/pintos-progos/tests/vm/page-merge-mm.ck b/pintos-progos/tests/vm/page-merge-mm.ck
new file mode 100644
index 0000000..74fa980
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-mm.ck
@@ -0,0 +1,29 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-merge-mm) begin
7(page-merge-mm) init
8(page-merge-mm) sort chunk 0
9(page-merge-mm) sort chunk 1
10(page-merge-mm) sort chunk 2
11(page-merge-mm) sort chunk 3
12(page-merge-mm) sort chunk 4
13(page-merge-mm) sort chunk 5
14(page-merge-mm) sort chunk 6
15(page-merge-mm) sort chunk 7
16(page-merge-mm) wait for child 0
17(page-merge-mm) wait for child 1
18(page-merge-mm) wait for child 2
19(page-merge-mm) wait for child 3
20(page-merge-mm) wait for child 4
21(page-merge-mm) wait for child 5
22(page-merge-mm) wait for child 6
23(page-merge-mm) wait for child 7
24(page-merge-mm) merge
25(page-merge-mm) verify
26(page-merge-mm) success, buf_idx=1,048,576
27(page-merge-mm) end
28EOF
29pass;
diff --git a/pintos-progos/tests/vm/page-merge-par.c b/pintos-progos/tests/vm/page-merge-par.c
new file mode 100644
index 0000000..e7e1609
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-par.c
@@ -0,0 +1,8 @@
1#include "tests/main.h"
2#include "tests/vm/parallel-merge.h"
3
4void
5test_main (void)
6{
7 parallel_merge ("child-sort", 123);
8}
diff --git a/pintos-progos/tests/vm/page-merge-par.ck b/pintos-progos/tests/vm/page-merge-par.ck
new file mode 100644
index 0000000..31f8aa7
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-par.ck
@@ -0,0 +1,29 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-merge-par) begin
7(page-merge-par) init
8(page-merge-par) sort chunk 0
9(page-merge-par) sort chunk 1
10(page-merge-par) sort chunk 2
11(page-merge-par) sort chunk 3
12(page-merge-par) sort chunk 4
13(page-merge-par) sort chunk 5
14(page-merge-par) sort chunk 6
15(page-merge-par) sort chunk 7
16(page-merge-par) wait for child 0
17(page-merge-par) wait for child 1
18(page-merge-par) wait for child 2
19(page-merge-par) wait for child 3
20(page-merge-par) wait for child 4
21(page-merge-par) wait for child 5
22(page-merge-par) wait for child 6
23(page-merge-par) wait for child 7
24(page-merge-par) merge
25(page-merge-par) verify
26(page-merge-par) success, buf_idx=1,048,576
27(page-merge-par) end
28EOF
29pass;
diff --git a/pintos-progos/tests/vm/page-merge-seq.c b/pintos-progos/tests/vm/page-merge-seq.c
new file mode 100644
index 0000000..12e3880
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-seq.c
@@ -0,0 +1,137 @@
1/* Generates about 1 MB of random data that is then divided into
2 16 chunks. A separate subprocess sorts each chunk in
3 sequence. Then we merge the chunks and verify that the result
4 is what it should be. */
5
6#include <syscall.h>
7#include "tests/arc4.h"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11/* This is the max file size for an older version of the Pintos
12 file system that had 126 direct blocks each pointing to a
13 single disk sector. We could raise it now. */
14#define CHUNK_SIZE (126 * 512)
15#define CHUNK_CNT 16 /* Number of chunks. */
16#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */
17
18unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE];
19size_t histogram[256];
20
21/* Initialize buf1 with random data,
22 then count the number of instances of each value within it. */
23static void
24init (void)
25{
26 struct arc4 arc4;
27 size_t i;
28
29 msg ("init");
30
31 arc4_init (&arc4, "foobar", 6);
32 arc4_crypt (&arc4, buf1, sizeof buf1);
33 for (i = 0; i < sizeof buf1; i++)
34 histogram[buf1[i]]++;
35}
36
37/* Sort each chunk of buf1 using a subprocess. */
38static void
39sort_chunks (void)
40{
41 size_t i;
42
43 create ("buffer", CHUNK_SIZE);
44 for (i = 0; i < CHUNK_CNT; i++)
45 {
46 pid_t child;
47 int handle;
48
49 msg ("sort chunk %zu", i);
50
51 /* Write this chunk to a file. */
52 quiet = true;
53 CHECK ((handle = open ("buffer")) > 1, "open \"buffer\"");
54 write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
55 close (handle);
56
57 /* Sort with subprocess. */
58 CHECK ((child = exec ("child-sort buffer")) != -1,
59 "exec \"child-sort buffer\"");
60 CHECK (wait (child) == 123, "wait for child-sort");
61
62 /* Read chunk back from file. */
63 CHECK ((handle = open ("buffer")) > 1, "open \"buffer\"");
64 read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
65 close (handle);
66
67 quiet = false;
68 }
69}
70
71/* Merge the sorted chunks in buf1 into a fully sorted buf2. */
72static void
73merge (void)
74{
75 unsigned char *mp[CHUNK_CNT];
76 size_t mp_left;
77 unsigned char *op;
78 size_t i;
79
80 msg ("merge");
81
82 /* Initialize merge pointers. */
83 mp_left = CHUNK_CNT;
84 for (i = 0; i < CHUNK_CNT; i++)
85 mp[i] = buf1 + CHUNK_SIZE * i;
86
87 /* Merge. */
88 op = buf2;
89 while (mp_left > 0)
90 {
91 /* Find smallest value. */
92 size_t min = 0;
93 for (i = 1; i < mp_left; i++)
94 if (*mp[i] < *mp[min])
95 min = i;
96
97 /* Append value to buf2. */
98 *op++ = *mp[min];
99
100 /* Advance merge pointer.
101 Delete this chunk from the set if it's emptied. */
102 if ((++mp[min] - buf1) % CHUNK_SIZE == 0)
103 mp[min] = mp[--mp_left];
104 }
105}
106
107static void
108verify (void)
109{
110 size_t buf_idx;
111 size_t hist_idx;
112
113 msg ("verify");
114
115 buf_idx = 0;
116 for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram;
117 hist_idx++)
118 {
119 while (histogram[hist_idx]-- > 0)
120 {
121 if (buf2[buf_idx] != hist_idx)
122 fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx);
123 buf_idx++;
124 }
125 }
126
127 msg ("success, buf_idx=%'zu", buf_idx);
128}
129
130void
131test_main (void)
132{
133 init ();
134 sort_chunks ();
135 merge ();
136 verify ();
137}
diff --git a/pintos-progos/tests/vm/page-merge-seq.ck b/pintos-progos/tests/vm/page-merge-seq.ck
new file mode 100644
index 0000000..d78f69d
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-seq.ck
@@ -0,0 +1,29 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-merge-seq) begin
7(page-merge-seq) init
8(page-merge-seq) sort chunk 0
9(page-merge-seq) sort chunk 1
10(page-merge-seq) sort chunk 2
11(page-merge-seq) sort chunk 3
12(page-merge-seq) sort chunk 4
13(page-merge-seq) sort chunk 5
14(page-merge-seq) sort chunk 6
15(page-merge-seq) sort chunk 7
16(page-merge-seq) sort chunk 8
17(page-merge-seq) sort chunk 9
18(page-merge-seq) sort chunk 10
19(page-merge-seq) sort chunk 11
20(page-merge-seq) sort chunk 12
21(page-merge-seq) sort chunk 13
22(page-merge-seq) sort chunk 14
23(page-merge-seq) sort chunk 15
24(page-merge-seq) merge
25(page-merge-seq) verify
26(page-merge-seq) success, buf_idx=1,032,192
27(page-merge-seq) end
28EOF
29pass;
diff --git a/pintos-progos/tests/vm/page-merge-stk.c b/pintos-progos/tests/vm/page-merge-stk.c
new file mode 100644
index 0000000..5eb1069
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-stk.c
@@ -0,0 +1,8 @@
1#include "tests/main.h"
2#include "tests/vm/parallel-merge.h"
3
4void
5test_main (void)
6{
7 parallel_merge ("child-qsort", 72);
8}
diff --git a/pintos-progos/tests/vm/page-merge-stk.ck b/pintos-progos/tests/vm/page-merge-stk.ck
new file mode 100644
index 0000000..c5bc1ae
--- /dev/null
+++ b/pintos-progos/tests/vm/page-merge-stk.ck
@@ -0,0 +1,29 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-merge-stk) begin
7(page-merge-stk) init
8(page-merge-stk) sort chunk 0
9(page-merge-stk) sort chunk 1
10(page-merge-stk) sort chunk 2
11(page-merge-stk) sort chunk 3
12(page-merge-stk) sort chunk 4
13(page-merge-stk) sort chunk 5
14(page-merge-stk) sort chunk 6
15(page-merge-stk) sort chunk 7
16(page-merge-stk) wait for child 0
17(page-merge-stk) wait for child 1
18(page-merge-stk) wait for child 2
19(page-merge-stk) wait for child 3
20(page-merge-stk) wait for child 4
21(page-merge-stk) wait for child 5
22(page-merge-stk) wait for child 6
23(page-merge-stk) wait for child 7
24(page-merge-stk) merge
25(page-merge-stk) verify
26(page-merge-stk) success, buf_idx=1,048,576
27(page-merge-stk) end
28EOF
29pass;
diff --git a/pintos-progos/tests/vm/page-parallel.c b/pintos-progos/tests/vm/page-parallel.c
new file mode 100644
index 0000000..9d619e0
--- /dev/null
+++ b/pintos-progos/tests/vm/page-parallel.c
@@ -0,0 +1,21 @@
1/* Runs 4 child-linear processes at once. */
2
3#include <syscall.h>
4#include "tests/lib.h"
5#include "tests/main.h"
6
7#define CHILD_CNT 4
8
9void
10test_main (void)
11{
12 pid_t children[CHILD_CNT];
13 int i;
14
15 for (i = 0; i < CHILD_CNT; i++)
16 CHECK ((children[i] = exec ("child-linear")) != -1,
17 "exec \"child-linear\"");
18
19 for (i = 0; i < CHILD_CNT; i++)
20 CHECK (wait (children[i]) == 0x42, "wait for child %d", i);
21}
diff --git a/pintos-progos/tests/vm/page-parallel.ck b/pintos-progos/tests/vm/page-parallel.ck
new file mode 100644
index 0000000..90c14ef
--- /dev/null
+++ b/pintos-progos/tests/vm/page-parallel.ck
@@ -0,0 +1,17 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(page-parallel) begin
7(page-parallel) exec "child-linear"
8(page-parallel) exec "child-linear"
9(page-parallel) exec "child-linear"
10(page-parallel) exec "child-linear"
11(page-parallel) wait for child 0
12(page-parallel) wait for child 1
13(page-parallel) wait for child 2
14(page-parallel) wait for child 3
15(page-parallel) end
16EOF
17pass;
diff --git a/pintos-progos/tests/vm/page-shuffle.c b/pintos-progos/tests/vm/page-shuffle.c
new file mode 100644
index 0000000..095a9da
--- /dev/null
+++ b/pintos-progos/tests/vm/page-shuffle.c
@@ -0,0 +1,30 @@
1/* Shuffles a 128 kB data buffer 10 times, printing the checksum
2 after each time. */
3
4#include <stdbool.h>
5#include "tests/arc4.h"
6#include "tests/cksum.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10#define SIZE (128 * 1024)
11
12static char buf[SIZE];
13
14void
15test_main (void)
16{
17 size_t i;
18
19 /* Initialize. */
20 for (i = 0; i < sizeof buf; i++)
21 buf[i] = i * 257;
22 msg ("init: cksum=%lu", cksum (buf, sizeof buf));
23
24 /* Shuffle repeatedly. */
25 for (i = 0; i < 10; i++)
26 {
27 shuffle (buf, sizeof buf, 1);
28 msg ("shuffle %zu: cksum=%lu", i, cksum (buf, sizeof buf));
29 }
30}
diff --git a/pintos-progos/tests/vm/page-shuffle.ck b/pintos-progos/tests/vm/page-shuffle.ck
new file mode 100644
index 0000000..6447d38
--- /dev/null
+++ b/pintos-progos/tests/vm/page-shuffle.ck
@@ -0,0 +1,44 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::cksum;
6use tests::lib;
7
8my ($init, @shuffle);
9if (1) {
10 # Use precalculated values.
11 $init = 3115322833;
12 @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467,
13 3022003332, 3614934266, 2704001777, 735775156, 1864109763);
14} else {
15 # Recalculate values.
16 my ($buf) = "";
17 for my $i (0...128 * 1024 - 1) {
18 $buf .= chr (($i * 257) & 0xff);
19 }
20 $init = cksum ($buf);
21
22 random_init (0);
23 for my $i (1...10) {
24 $buf = shuffle ($buf, length ($buf), 1);
25 push (@shuffle, cksum ($buf));
26 }
27}
28
29check_expected (IGNORE_EXIT_CODES => 1, [<<EOF]);
30(page-shuffle) begin
31(page-shuffle) init: cksum=$init
32(page-shuffle) shuffle 0: cksum=$shuffle[0]
33(page-shuffle) shuffle 1: cksum=$shuffle[1]
34(page-shuffle) shuffle 2: cksum=$shuffle[2]
35(page-shuffle) shuffle 3: cksum=$shuffle[3]
36(page-shuffle) shuffle 4: cksum=$shuffle[4]
37(page-shuffle) shuffle 5: cksum=$shuffle[5]
38(page-shuffle) shuffle 6: cksum=$shuffle[6]
39(page-shuffle) shuffle 7: cksum=$shuffle[7]
40(page-shuffle) shuffle 8: cksum=$shuffle[8]
41(page-shuffle) shuffle 9: cksum=$shuffle[9]
42(page-shuffle) end
43EOF
44pass;
diff --git a/pintos-progos/tests/vm/parallel-merge.c b/pintos-progos/tests/vm/parallel-merge.c
new file mode 100644
index 0000000..cc09bb1
--- /dev/null
+++ b/pintos-progos/tests/vm/parallel-merge.c
@@ -0,0 +1,149 @@
1/* Generates about 1 MB of random data that is then divided into
2 16 chunks. A separate subprocess sorts each chunk; the
3 subprocesses run in parallel. Then we merge the chunks and
4 verify that the result is what it should be. */
5
6#include "tests/vm/parallel-merge.h"
7#include <stdio.h>
8#include <syscall.h>
9#include "tests/arc4.h"
10#include "tests/lib.h"
11#include "tests/main.h"
12
13#define CHUNK_SIZE (128 * 1024)
14#define CHUNK_CNT 8 /* Number of chunks. */
15#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE) /* Buffer size. */
16
17unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE];
18size_t histogram[256];
19
20/* Initialize buf1 with random data,
21 then count the number of instances of each value within it. */
22static void
23init (void)
24{
25 struct arc4 arc4;
26 size_t i;
27
28 msg ("init");
29
30 arc4_init (&arc4, "foobar", 6);
31 arc4_crypt (&arc4, buf1, sizeof buf1);
32 for (i = 0; i < sizeof buf1; i++)
33 histogram[buf1[i]]++;
34}
35
36/* Sort each chunk of buf1 using SUBPROCESS,
37 which is expected to return EXIT_STATUS. */
38static void
39sort_chunks (const char *subprocess, int exit_status)
40{
41 pid_t children[CHUNK_CNT];
42 size_t i;
43
44 for (i = 0; i < CHUNK_CNT; i++)
45 {
46 char fn[128];
47 char cmd[128];
48 int handle;
49
50 msg ("sort chunk %zu", i);
51
52 /* Write this chunk to a file. */
53 snprintf (fn, sizeof fn, "buf%zu", i);
54 create (fn, CHUNK_SIZE);
55 quiet = true;
56 CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
57 write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
58 close (handle);
59
60 /* Sort with subprocess. */
61 snprintf (cmd, sizeof cmd, "%s %s", subprocess, fn);
62 CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd);
63 quiet = false;
64 }
65
66 for (i = 0; i < CHUNK_CNT; i++)
67 {
68 char fn[128];
69 int handle;
70
71 CHECK (wait (children[i]) == exit_status, "wait for child %zu", i);
72
73 /* Read chunk back from file. */
74 quiet = true;
75 snprintf (fn, sizeof fn, "buf%zu", i);
76 CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
77 read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
78 close (handle);
79 quiet = false;
80 }
81}
82
83/* Merge the sorted chunks in buf1 into a fully sorted buf2. */
84static void
85merge (void)
86{
87 unsigned char *mp[CHUNK_CNT];
88 size_t mp_left;
89 unsigned char *op;
90 size_t i;
91
92 msg ("merge");
93
94 /* Initialize merge pointers. */
95 mp_left = CHUNK_CNT;
96 for (i = 0; i < CHUNK_CNT; i++)
97 mp[i] = buf1 + CHUNK_SIZE * i;
98
99 /* Merge. */
100 op = buf2;
101 while (mp_left > 0)
102 {
103 /* Find smallest value. */
104 size_t min = 0;
105 for (i = 1; i < mp_left; i++)
106 if (*mp[i] < *mp[min])
107 min = i;
108
109 /* Append value to buf2. */
110 *op++ = *mp[min];
111
112 /* Advance merge pointer.
113 Delete this chunk from the set if it's emptied. */
114 if ((++mp[min] - buf1) % CHUNK_SIZE == 0)
115 mp[min] = mp[--mp_left];
116 }
117}
118
119static void
120verify (void)
121{
122 size_t buf_idx;
123 size_t hist_idx;
124
125 msg ("verify");
126
127 buf_idx = 0;
128 for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram;
129 hist_idx++)
130 {
131 while (histogram[hist_idx]-- > 0)
132 {
133 if (buf2[buf_idx] != hist_idx)
134 fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx);
135 buf_idx++;
136 }
137 }
138
139 msg ("success, buf_idx=%'zu", buf_idx);
140}
141
142void
143parallel_merge (const char *child_name, int exit_status)
144{
145 init ();
146 sort_chunks (child_name, exit_status);
147 merge ();
148 verify ();
149}
diff --git a/pintos-progos/tests/vm/parallel-merge.h b/pintos-progos/tests/vm/parallel-merge.h
new file mode 100644
index 0000000..a6b6431
--- /dev/null
+++ b/pintos-progos/tests/vm/parallel-merge.h
@@ -0,0 +1,6 @@
1#ifndef TESTS_VM_PARALLEL_MERGE
2#define TESTS_VM_PARALLEL_MERGE 1
3
4void parallel_merge (const char *child_name, int exit_status);
5
6#endif /* tests/vm/parallel-merge.h */
diff --git a/pintos-progos/tests/vm/process_death.pm b/pintos-progos/tests/vm/process_death.pm
new file mode 100644
index 0000000..52039a1
--- /dev/null
+++ b/pintos-progos/tests/vm/process_death.pm
@@ -0,0 +1,22 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5
6sub check_process_death {
7 my ($proc_name) = @_;
8 our ($test);
9 my (@output) = read_text_file ("$test.output");
10
11 common_checks ("run", @output);
12 @output = get_core_output ("run", @output);
13 fail "First line of output is not `($proc_name) begin' message.\n"
14 if $output[0] ne "($proc_name) begin";
15 fail "Output missing '$proc_name: exit(-1)' message.\n"
16 if !grep ("$proc_name: exit(-1)" eq $_, @output);
17 fail "Output contains '($proc_name) end' message.\n"
18 if grep (/\($proc_name\) end/, @output);
19 pass;
20}
21
221;
diff --git a/pintos-progos/tests/vm/pt-bad-addr.c b/pintos-progos/tests/vm/pt-bad-addr.c
new file mode 100644
index 0000000..3ca4084
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-bad-addr.c
@@ -0,0 +1,11 @@
1/* Accesses a bad address.
2 The process must be terminated with -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 fail ("bad addr read as %d", *(int *) 0x04000000);
11}
diff --git a/pintos-progos/tests/vm/pt-bad-addr.ck b/pintos-progos/tests/vm/pt-bad-addr.ck
new file mode 100644
index 0000000..09ea039
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-bad-addr.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::vm::process_death;
6
7check_process_death ('pt-bad-addr');
diff --git a/pintos-progos/tests/vm/pt-bad-read.c b/pintos-progos/tests/vm/pt-bad-read.c
new file mode 100644
index 0000000..ee791ff
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-bad-read.c
@@ -0,0 +1,16 @@
1/* Reads from a file into a bad address.
2 The process must be terminated with -1 exit code. */
3
4#include <syscall.h>
5#include "tests/lib.h"
6#include "tests/main.h"
7
8void
9test_main (void)
10{
11 int handle;
12
13 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
14 read (handle, (char *) &handle - 4096, 1);
15 fail ("survived reading data into bad address");
16}
diff --git a/pintos-progos/tests/vm/pt-bad-read.ck b/pintos-progos/tests/vm/pt-bad-read.ck
new file mode 100644
index 0000000..1f96bb4
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-bad-read.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(pt-bad-read) begin
7(pt-bad-read) open "sample.txt"
8pt-bad-read: exit(-1)
9EOF
10pass;
diff --git a/pintos-progos/tests/vm/pt-big-stk-obj.c b/pintos-progos/tests/vm/pt-big-stk-obj.c
new file mode 100644
index 0000000..6b630ec
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-big-stk-obj.c
@@ -0,0 +1,20 @@
1/* Allocates and writes to a 64 kB object on the stack.
2 This must succeed. */
3
4#include <string.h>
5#include "tests/arc4.h"
6#include "tests/cksum.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char stk_obj[65536];
14 struct arc4 arc4;
15
16 arc4_init (&arc4, "foobar", 6);
17 memset (stk_obj, 0, sizeof stk_obj);
18 arc4_crypt (&arc4, stk_obj, sizeof stk_obj);
19 msg ("cksum: %lu", cksum (stk_obj, sizeof stk_obj));
20}
diff --git a/pintos-progos/tests/vm/pt-big-stk-obj.ck b/pintos-progos/tests/vm/pt-big-stk-obj.ck
new file mode 100644
index 0000000..eb5853a
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-big-stk-obj.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(pt-big-stk-obj) begin
7(pt-big-stk-obj) cksum: 3256410166
8(pt-big-stk-obj) end
9EOF
10pass;
diff --git a/pintos-progos/tests/vm/pt-grow-bad.c b/pintos-progos/tests/vm/pt-grow-bad.c
new file mode 100644
index 0000000..d4beba2
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-bad.c
@@ -0,0 +1,14 @@
1/* Read from an address 4,096 bytes below the stack pointer.
2 The process must be terminated with -1 exit code. */
3
4#include <string.h>
5#include "tests/arc4.h"
6#include "tests/cksum.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 asm volatile ("movl -4096(%esp), %eax");
14}
diff --git a/pintos-progos/tests/vm/pt-grow-bad.ck b/pintos-progos/tests/vm/pt-grow-bad.ck
new file mode 100644
index 0000000..4c0ab8a
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-bad.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
6(pt-grow-bad) begin
7pt-grow-bad: exit(-1)
8EOF
9pass;
diff --git a/pintos-progos/tests/vm/pt-grow-pusha.c b/pintos-progos/tests/vm/pt-grow-pusha.c
new file mode 100644
index 0000000..f9762a5
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-pusha.c
@@ -0,0 +1,20 @@
1/* Expand the stack by 32 bytes all at once using the PUSHA
2 instruction.
3 This must succeed. */
4
5#include <string.h>
6#include "tests/arc4.h"
7#include "tests/cksum.h"
8#include "tests/lib.h"
9#include "tests/main.h"
10
11void
12test_main (void)
13{
14 asm volatile
15 ("movl %%esp, %%eax;" /* Save a copy of the stack pointer. */
16 "andl $0xfffff000, %%esp;" /* Move stack pointer to bottom of page. */
17 "pushal;" /* Push 32 bytes on stack at once. */
18 "movl %%eax, %%esp" /* Restore copied stack pointer. */
19 : : : "eax"); /* Tell GCC we destroyed eax. */
20}
diff --git a/pintos-progos/tests/vm/pt-grow-pusha.ck b/pintos-progos/tests/vm/pt-grow-pusha.ck
new file mode 100644
index 0000000..5000966
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-pusha.ck
@@ -0,0 +1,9 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(pt-grow-pusha) begin
7(pt-grow-pusha) end
8EOF
9pass;
diff --git a/pintos-progos/tests/vm/pt-grow-stack.c b/pintos-progos/tests/vm/pt-grow-stack.c
new file mode 100644
index 0000000..0997a00
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-stack.c
@@ -0,0 +1,20 @@
1/* Demonstrate that the stack can grow.
2 This must succeed. */
3
4#include <string.h>
5#include "tests/arc4.h"
6#include "tests/cksum.h"
7#include "tests/lib.h"
8#include "tests/main.h"
9
10void
11test_main (void)
12{
13 char stack_obj[4096];
14 struct arc4 arc4;
15
16 arc4_init (&arc4, "foobar", 6);
17 memset (stack_obj, 0, sizeof stack_obj);
18 arc4_crypt (&arc4, stack_obj, sizeof stack_obj);
19 msg ("cksum: %lu", cksum (stack_obj, sizeof stack_obj));
20}
diff --git a/pintos-progos/tests/vm/pt-grow-stack.ck b/pintos-progos/tests/vm/pt-grow-stack.ck
new file mode 100644
index 0000000..1e669db
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-stack.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(pt-grow-stack) begin
7(pt-grow-stack) cksum: 3424492700
8(pt-grow-stack) end
9EOF
10pass;
diff --git a/pintos-progos/tests/vm/pt-grow-stk-sc.c b/pintos-progos/tests/vm/pt-grow-stk-sc.c
new file mode 100644
index 0000000..3efbb5f
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-stk-sc.c
@@ -0,0 +1,32 @@
1/* This test checks that the stack is properly extended even if
2 the first access to a stack location occurs inside a system
3 call.
4
5 From Godmar Back. */
6
7#include <string.h>
8#include <syscall.h>
9#include "tests/vm/sample.inc"
10#include "tests/lib.h"
11#include "tests/main.h"
12
13void
14test_main (void)
15{
16 int handle;
17 int slen = strlen (sample);
18 char buf2[65536];
19
20 /* Write file via write(). */
21 CHECK (create ("sample.txt", slen), "create \"sample.txt\"");
22 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
23 CHECK (write (handle, sample, slen) == slen, "write \"sample.txt\"");
24 close (handle);
25
26 /* Read back via read(). */
27 CHECK ((handle = open ("sample.txt")) > 1, "2nd open \"sample.txt\"");
28 CHECK (read (handle, buf2 + 32768, slen) == slen, "read \"sample.txt\"");
29
30 CHECK (!memcmp (sample, buf2 + 32768, slen), "compare written data against read data");
31 close (handle);
32}
diff --git a/pintos-progos/tests/vm/pt-grow-stk-sc.ck b/pintos-progos/tests/vm/pt-grow-stk-sc.ck
new file mode 100644
index 0000000..23d3b02
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-grow-stk-sc.ck
@@ -0,0 +1,15 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
6(pt-grow-stk-sc) begin
7(pt-grow-stk-sc) create "sample.txt"
8(pt-grow-stk-sc) open "sample.txt"
9(pt-grow-stk-sc) write "sample.txt"
10(pt-grow-stk-sc) 2nd open "sample.txt"
11(pt-grow-stk-sc) read "sample.txt"
12(pt-grow-stk-sc) compare written data against read data
13(pt-grow-stk-sc) end
14EOF
15pass;
diff --git a/pintos-progos/tests/vm/pt-write-code-2.c b/pintos-progos/tests/vm/pt-write-code-2.c
new file mode 100644
index 0000000..83bcc2c
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-write-code-2.c
@@ -0,0 +1,15 @@
1/* Try to write to the code segment using a system call.
2 The process must be terminated with -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 int handle;
11
12 CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
13 read (handle, (void *) test_main, 1);
14 fail ("survived reading data into code segment");
15}
diff --git a/pintos-progos/tests/vm/pt-write-code.c b/pintos-progos/tests/vm/pt-write-code.c
new file mode 100644
index 0000000..5072cec
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-write-code.c
@@ -0,0 +1,12 @@
1/* Try to write to the code segment.
2 The process must be terminated with -1 exit code. */
3
4#include "tests/lib.h"
5#include "tests/main.h"
6
7void
8test_main (void)
9{
10 *(int *) test_main = 0;
11 fail ("writing the code segment succeeded");
12}
diff --git a/pintos-progos/tests/vm/pt-write-code.ck b/pintos-progos/tests/vm/pt-write-code.ck
new file mode 100644
index 0000000..65610fb
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-write-code.ck
@@ -0,0 +1,7 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5use tests::vm::process_death;
6
7check_process_death ('pt-write-code');
diff --git a/pintos-progos/tests/vm/pt-write-code2.ck b/pintos-progos/tests/vm/pt-write-code2.ck
new file mode 100644
index 0000000..69ffc77
--- /dev/null
+++ b/pintos-progos/tests/vm/pt-write-code2.ck
@@ -0,0 +1,10 @@
1# -*- perl -*-
2use strict;
3use warnings;
4use tests::tests;
5check_expected ([<<'EOF']);
6(pt-write-code2) begin
7(pt-write-code2) open "sample.txt"
8pt-write-code2: exit(-1)
9EOF
10pass;
diff --git a/pintos-progos/tests/vm/qsort.c b/pintos-progos/tests/vm/qsort.c
new file mode 100644
index 0000000..922572c
--- /dev/null
+++ b/pintos-progos/tests/vm/qsort.c
@@ -0,0 +1,136 @@
1#include "tests/vm/qsort.h"
2#include <stdbool.h>
3#include <debug.h>
4#include <random.h>
5
6/* Picks a pivot for the quicksort from the SIZE bytes in BUF. */
7static unsigned char
8pick_pivot (unsigned char *buf, size_t size)
9{
10 ASSERT (size >= 1);
11 return buf[random_ulong () % size];
12}
13
14/* Checks whether the SIZE bytes in ARRAY are divided into an
15 initial LEFT_SIZE elements all less than PIVOT followed by
16 SIZE - LEFT_SIZE elements all greater than or equal to
17 PIVOT. */
18static bool
19is_partitioned (const unsigned char *array, size_t size,
20 unsigned char pivot, size_t left_size)
21{
22 size_t i;
23
24 for (i = 0; i < left_size; i++)
25 if (array[i] >= pivot)
26 return false;
27
28 for (; i < size; i++)
29 if (array[i] < pivot)
30 return false;
31
32 return true;
33}
34
35/* Swaps the bytes at *A and *B. */
36static void
37swap (unsigned char *a, unsigned char *b)
38{
39 unsigned char t = *a;
40 *a = *b;
41 *b = t;
42}
43
44/* Partitions ARRAY in-place in an initial run of bytes all less
45 than PIVOT, followed by a run of bytes all greater than or
46 equal to PIVOT. Returns the length of the initial run. */
47static size_t
48partition (unsigned char *array, size_t size, int pivot)
49{
50 size_t left_size = size;
51 unsigned char *first = array;
52 unsigned char *last = first + left_size;
53
54 for (;;)
55 {
56 /* Move FIRST forward to point to first element greater than
57 PIVOT. */
58 for (;;)
59 {
60 if (first == last)
61 {
62 ASSERT (is_partitioned (array, size, pivot, left_size));
63 return left_size;
64 }
65 else if (*first >= pivot)
66 break;
67
68 first++;
69 }
70 left_size--;
71
72 /* Move LAST backward to point to last element no bigger
73 than PIVOT. */
74 for (;;)
75 {
76 last--;
77
78 if (first == last)
79 {
80 ASSERT (is_partitioned (array, size, pivot, left_size));
81 return left_size;
82 }
83 else if (*last < pivot)
84 break;
85 else
86 left_size--;
87 }
88
89 /* By swapping FIRST and LAST we extend the starting and
90 ending sequences that pass and fail, respectively,
91 PREDICATE. */
92 swap (first, last);
93 first++;
94 }
95}
96
97/* Returns true if the SIZE bytes in BUF are in nondecreasing
98 order, false otherwise. */
99static bool
100is_sorted (const unsigned char *buf, size_t size)
101{
102 size_t i;
103
104 for (i = 1; i < size; i++)
105 if (buf[i - 1] > buf[i])
106 return false;
107
108 return true;
109}
110
111/* Sorts the SIZE bytes in BUF into nondecreasing order, using
112 the quick-sort algorithm. */
113void
114qsort_bytes (unsigned char *buf, size_t size)
115{
116 if (!is_sorted (buf, size))
117 {
118 int pivot = pick_pivot (buf, size);
119
120 unsigned char *left_half = buf;
121 size_t left_size = partition (buf, size, pivot);
122 unsigned char *right_half = left_half + left_size;
123 size_t right_size = size - left_size;
124
125 if (left_size <= right_size)
126 {
127 qsort_bytes (left_half, left_size);
128 qsort_bytes (right_half, right_size);
129 }
130 else
131 {
132 qsort_bytes (right_half, right_size);
133 qsort_bytes (left_half, left_size);
134 }
135 }
136}
diff --git a/pintos-progos/tests/vm/qsort.h b/pintos-progos/tests/vm/qsort.h
new file mode 100644
index 0000000..61b65f3
--- /dev/null
+++ b/pintos-progos/tests/vm/qsort.h
@@ -0,0 +1,8 @@
1#ifndef TESTS_VM_QSORT_H
2#define TESTS_VM_QSORT_H 1
3
4#include <stddef.h>
5
6void qsort_bytes (unsigned char *buf, size_t size);
7
8#endif /* tests/vm/qsort.h */
diff --git a/pintos-progos/tests/vm/sample.inc b/pintos-progos/tests/vm/sample.inc
new file mode 100644
index 0000000..a60a139
--- /dev/null
+++ b/pintos-progos/tests/vm/sample.inc
@@ -0,0 +1,19 @@
1char sample[] = {
2 "=== ALL USERS PLEASE NOTE ========================\n"
3 "\n"
4 "CAR and CDR now return extra values.\n"
5 "\n"
6 "The function CAR now returns two values. Since it has to go to the\n"
7 "trouble to figure out if the object is carcdr-able anyway, we figured\n"
8 "you might as well get both halves at once. For example, the following\n"
9 "code shows how to destructure a cons (SOME-CONS) into its two slots\n"
10 "(THE-CAR and THE-CDR):\n"
11 "\n"
12 " (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)\n"
13 "\n"
14 "For symmetry with CAR, CDR returns a second value which is the CAR of\n"
15 "the object. In a related change, the functions MAKE-ARRAY and CONS\n"
16 "have been fixed so they don't allocate any storage except on the\n"
17 "stack. This should hopefully help people who don't like using the\n"
18 "garbage collector because it cold boots the machine so often.\n"
19};
diff --git a/pintos-progos/tests/vm/sample.txt b/pintos-progos/tests/vm/sample.txt
new file mode 100644
index 0000000..c446830
--- /dev/null
+++ b/pintos-progos/tests/vm/sample.txt
@@ -0,0 +1,17 @@
1=== ALL USERS PLEASE NOTE ========================
2
3CAR and CDR now return extra values.
4
5The function CAR now returns two values. Since it has to go to the
6trouble to figure out if the object is carcdr-able anyway, we figured
7you might as well get both halves at once. For example, the following
8code shows how to destructure a cons (SOME-CONS) into its two slots
9(THE-CAR and THE-CDR):
10
11 (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)
12
13For symmetry with CAR, CDR returns a second value which is the CAR of
14the object. In a related change, the functions MAKE-ARRAY and CONS
15have been fixed so they don't allocate any storage except on the
16stack. This should hopefully help people who don't like using the
17garbage collector because it cold boots the machine so often.
diff --git a/pintos-progos/threads/.gitignore b/pintos-progos/threads/.gitignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/pintos-progos/threads/.gitignore
@@ -0,0 +1,3 @@
1build
2bochsrc.txt
3bochsout.txt
diff --git a/pintos-progos/threads/Make.vars b/pintos-progos/threads/Make.vars
new file mode 100644
index 0000000..310c240
--- /dev/null
+++ b/pintos-progos/threads/Make.vars
@@ -0,0 +1,7 @@
1# -*- makefile -*-
2
3kernel.bin: DEFINES =
4KERNEL_SUBDIRS = threads devices lib lib/kernel $(TEST_SUBDIRS)
5TEST_SUBDIRS = tests/threads
6GRADING_FILE = $(SRCDIR)/tests/threads/Grading
7SIMULATOR = --bochs
diff --git a/pintos-progos/threads/Makefile b/pintos-progos/threads/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/pintos-progos/threads/Makefile
@@ -0,0 +1 @@
include ../Makefile.kernel
diff --git a/pintos-progos/threads/flags.h b/pintos-progos/threads/flags.h
new file mode 100644
index 0000000..5654ac7
--- /dev/null
+++ b/pintos-progos/threads/flags.h
@@ -0,0 +1,8 @@
1#ifndef THREADS_FLAGS_H
2#define THREADS_FLAGS_H
3
4/* EFLAGS Register. */
5#define FLAG_MBS 0x00000002 /* Must be set. */
6#define FLAG_IF 0x00000200 /* Interrupt Flag. */
7
8#endif /* threads/flags.h */
diff --git a/pintos-progos/threads/init.c b/pintos-progos/threads/init.c
new file mode 100644
index 0000000..d8feacd
--- /dev/null
+++ b/pintos-progos/threads/init.c
@@ -0,0 +1,453 @@
1#include "threads/init.h"
2#include <console.h>
3#include <debug.h>
4#include <inttypes.h>
5#include <limits.h>
6#include <random.h>
7#include <stddef.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include "devices/kbd.h"
12#include "devices/input.h"
13#include "devices/serial.h"
14#include "devices/shutdown.h"
15#include "devices/timer.h"
16#include "devices/vga.h"
17#include "devices/rtc.h"
18#include "threads/interrupt.h"
19#include "threads/io.h"
20#include "threads/loader.h"
21#include "threads/malloc.h"
22#include "threads/palloc.h"
23#include "threads/pte.h"
24#include "threads/thread.h"
25#ifdef USERPROG
26#include "userprog/process.h"
27#include "userprog/exception.h"
28#include "userprog/gdt.h"
29#include "userprog/syscall.h"
30#include "userprog/tss.h"
31#else
32#include "tests/threads/tests.h"
33#endif
34#ifdef FILESYS
35#include "devices/block.h"
36#include "devices/ide.h"
37#include "filesys/filesys.h"
38#include "filesys/fsutil.h"
39#endif
40
41
42/* Page directory with kernel mappings only. */
43uint32_t *init_page_dir;
44
45#ifdef FILESYS
46/* -f: Format the file system? */
47static bool format_filesys;
48
49/* -filesys, -scratch, -swap: Names of block devices to use,
50 overriding the defaults. */
51static const char *filesys_bdev_name;
52static const char *scratch_bdev_name;
53#ifdef VM
54static const char *swap_bdev_name;
55#endif
56#endif /* FILESYS */
57
58/* -kernel-test: Run kernel test instead of user program */
59static bool kernel_test = false;
60
61/* provide weak kernel test definition if no test is available */
62void run_test (const char *param);
63__attribute__((weak))
64void
65run_test (const char *param UNUSED)
66{
67 printf("No kernel test linked into kernel\n");
68}
69
70
71/* -ul: Maximum number of pages to put into palloc's user pool. */
72static size_t user_page_limit = SIZE_MAX;
73
74static void bss_init (void);
75static void paging_init (void);
76
77static char **read_command_line (void);
78static char **parse_options (char **argv);
79static void run_actions (char **argv);
80static void usage (void);
81
82#ifdef FILESYS
83static void locate_block_devices (void);
84static void locate_block_device (enum block_type, const char *name);
85#endif
86
87int main (void) NO_RETURN;
88
89/* Pintos main program. */
90int
91main (void)
92{
93 char **argv;
94
95 /* Clear BSS. */
96 bss_init ();
97
98 /* Break command line into arguments and parse options. */
99 argv = read_command_line ();
100 argv = parse_options (argv);
101
102 /* Initialize ourselves as a thread so we can use locks,
103 then enable console locking. */
104 thread_init ();
105 console_init ();
106
107 /* Greet user. */
108 printf ("Pintos booting with %'"PRIu32" kB RAM...\n",
109 init_ram_pages * PGSIZE / 1024);
110
111 /* Initialize memory system. */
112 palloc_init (user_page_limit);
113 malloc_init ();
114 paging_init ();
115
116 /* Segmentation. */
117#ifdef USERPROG
118 tss_init ();
119 gdt_init ();
120#endif
121
122 /* Initialize interrupt handlers. */
123 intr_init ();
124 timer_init ();
125 kbd_init ();
126 input_init ();
127#ifdef USERPROG
128 exception_init ();
129 syscall_init ();
130#endif
131
132 /* Start thread scheduler and enable interrupts. */
133 thread_start ();
134 serial_init_queue ();
135 timer_calibrate ();
136
137#ifdef FILESYS
138 /* Initialize file system. */
139 ide_init ();
140 locate_block_devices ();
141 /* kernel tests do not need filesystem */
142 if (!kernel_test)
143 filesys_init (format_filesys);
144#endif
145
146 printf ("Boot complete.\n");
147
148 /* Run actions specified on kernel command line. */
149 run_actions (argv);
150
151 /* Finish up. */
152 shutdown ();
153 thread_exit ();
154}
155
156/* Clear the "BSS", a segment that should be initialized to
157 zeros. It isn't actually stored on disk or zeroed by the
158 kernel loader, so we have to zero it ourselves.
159
160 The start and end of the BSS segment is recorded by the
161 linker as _start_bss and _end_bss. See kernel.lds. */
162static void
163bss_init (void)
164{
165 extern char _start_bss, _end_bss;
166 memset (&_start_bss, 0, &_end_bss - &_start_bss);
167}
168
169/* Populates the base page directory and page table with the
170 kernel virtual mapping, and then sets up the CPU to use the
171 new page directory. Points init_page_dir to the page
172 directory it creates. */
173static void
174paging_init (void)
175{
176 uint32_t *pd, *pt;
177 size_t page;
178 extern char _start, _end_kernel_text;
179
180 pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
181 pt = NULL;
182 for (page = 0; page < init_ram_pages; page++)
183 {
184 uintptr_t paddr = page * PGSIZE;
185 char *vaddr = ptov (paddr);
186 size_t pde_idx = pd_no (vaddr);
187 size_t pte_idx = pt_no (vaddr);
188 bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text;
189
190 if (pd[pde_idx] == 0)
191 {
192 pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
193 pd[pde_idx] = pde_create (pt);
194 }
195
196 pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
197 }
198
199 /* Store the physical address of the page directory into CR3
200 aka PDBR (page directory base register). This activates our
201 new page tables immediately. See [IA32-v2a] "MOV--Move
202 to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
203 of the Page Directory". */
204 asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir)));
205}
206
207/* Breaks the kernel command line into words and returns them as
208 an argv-like array. */
209static char **
210read_command_line (void)
211{
212 static char *argv[LOADER_ARGS_LEN / 2 + 1];
213 char *p, *end;
214 int argc;
215 int i;
216
217 argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
218 p = ptov (LOADER_ARGS);
219 end = p + LOADER_ARGS_LEN;
220 for (i = 0; i < argc; i++)
221 {
222 if (p >= end)
223 PANIC ("command line arguments overflow");
224
225 argv[i] = p;
226 p += strnlen (p, end - p) + 1;
227 }
228 argv[argc] = NULL;
229
230 /* Print kernel command line. */
231 printf ("Kernel command line:");
232 for (i = 0; i < argc; i++)
233 if (strchr (argv[i], ' ') == NULL)
234 printf (" %s", argv[i]);
235 else
236 printf (" '%s'", argv[i]);
237 printf ("\n");
238
239 return argv;
240}
241
242/* Parses options in ARGV[]
243 and returns the first non-option argument. */
244static char **
245parse_options (char **argv)
246{
247 for (; *argv != NULL && **argv == '-'; argv++)
248 {
249 char *save_ptr;
250 char *name = strtok_r (*argv, "=", &save_ptr);
251 char *value = strtok_r (NULL, "", &save_ptr);
252
253#ifndef USERPROG
254 kernel_test = true;
255#endif
256 if (!strcmp (name, "-h"))
257 usage ();
258 else if (!strcmp (name, "-q"))
259 shutdown_configure (SHUTDOWN_POWER_OFF);
260 else if (!strcmp (name, "-r"))
261 shutdown_configure (SHUTDOWN_REBOOT);
262#ifdef FILESYS
263 else if (!strcmp (name, "-f"))
264 format_filesys = true;
265 else if (!strcmp (name, "-filesys"))
266 filesys_bdev_name = value;
267 else if (!strcmp (name, "-scratch"))
268 scratch_bdev_name = value;
269#ifdef VM
270 else if (!strcmp (name, "-swap"))
271 swap_bdev_name = value;
272#endif
273#endif
274 else if (!strcmp (name, "-rs"))
275 random_init (atoi (value));
276 else if (!strcmp (name, "-mlfqs"))
277 thread_mlfqs = true;
278#ifdef USERPROG
279 else if (!strcmp (name, "-kernel-test"))
280 kernel_test = true;
281 else if (!strcmp (name, "-ul"))
282 user_page_limit = atoi (value);
283#endif
284 else
285 PANIC ("unknown option `%s' (use -h for help)", name);
286 }
287
288 /* Initialize the random number generator based on the system
289 time. This has no effect if an "-rs" option was specified.
290
291 When running under Bochs, this is not enough by itself to
292 get a good seed value, because the pintos script sets the
293 initial time to a predictable value, not to the local time,
294 for reproducibility. To fix this, give the "-r" option to
295 the pintos script to request real-time execution. */
296 random_init (rtc_get_time ());
297
298 return argv;
299}
300
301/* Runs the task specified in ARGV[1]. */
302static void
303run_task (char **argv)
304{
305 const char *task = argv[1];
306
307 printf ("Executing '%s':\n", task);
308#ifdef USERPROG
309 if (kernel_test)
310 run_test (task);
311 else
312 process_wait (process_execute (task));
313#else
314 run_test (task);
315#endif
316 printf ("Execution of '%s' complete.\n", task);
317}
318
319/* Executes all of the actions specified in ARGV[]
320 up to the null pointer sentinel. */
321static void
322run_actions (char **argv)
323{
324 /* An action. */
325 struct action
326 {
327 char *name; /* Action name. */
328 int argc; /* # of args, including action name. */
329 void (*function) (char **argv); /* Function to execute action. */
330 };
331
332 /* Table of supported actions. */
333 static const struct action actions[] =
334 {
335 {"run", 2, run_task},
336#ifdef FILESYS
337 {"ls", 1, fsutil_ls},
338 {"cat", 2, fsutil_cat},
339 {"rm", 2, fsutil_rm},
340 {"extract", 1, fsutil_extract},
341 {"append", 2, fsutil_append},
342#endif
343 {NULL, 0, NULL},
344 };
345
346 while (*argv != NULL)
347 {
348 const struct action *a;
349 int i;
350
351 /* Find action name. */
352 for (a = actions; ; a++)
353 if (a->name == NULL)
354 PANIC ("unknown action `%s' (use -h for help)", *argv);
355 else if (!strcmp (*argv, a->name))
356 break;
357
358 /* Check for required arguments. */
359 for (i = 1; i < a->argc; i++)
360 if (argv[i] == NULL)
361 PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
362
363 /* Invoke action and advance. */
364 a->function (argv);
365 argv += a->argc;
366 }
367
368}
369
370/* Prints a kernel command line help message and powers off the
371 machine. */
372static void
373usage (void)
374{
375 printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
376 "Options must precede actions.\n"
377 "Actions are executed in the order specified.\n"
378 "\nAvailable actions:\n"
379#ifdef USERPROG
380 " run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
381#else
382 " run TEST Run TEST.\n"
383#endif
384#ifdef FILESYS
385 " ls List files in the root directory.\n"
386 " cat FILE Print FILE to the console.\n"
387 " rm FILE Delete FILE.\n"
388 "Use these actions indirectly via `pintos' -g and -p options:\n"
389 " extract Untar from scratch device into file system.\n"
390 " append FILE Append FILE to tar file on scratch device.\n"
391#endif
392 "\nOptions:\n"
393 " -h Print this help message and power off.\n"
394 " -q Power off VM after actions or on panic.\n"
395 " -r Reboot after actions.\n"
396#ifdef FILESYS
397 " -f Format file system device during startup.\n"
398 " -filesys=BDEV Use BDEV for file system instead of default.\n"
399 " -scratch=BDEV Use BDEV for scratch instead of default.\n"
400#ifdef VM
401 " -swap=BDEV Use BDEV for swap instead of default.\n"
402#endif
403#endif
404 " -rs=SEED Set random number seed to SEED.\n"
405 " -mlfqs Use multi-level feedback queue scheduler.\n"
406#ifdef USERPROG
407 " -ul=COUNT Limit user memory to COUNT pages.\n"
408#endif
409 );
410 shutdown_power_off ();
411}
412
413#ifdef FILESYS
414/* Figure out what block devices to cast in the various Pintos roles. */
415static void
416locate_block_devices (void)
417{
418 locate_block_device (BLOCK_FILESYS, filesys_bdev_name);
419 locate_block_device (BLOCK_SCRATCH, scratch_bdev_name);
420#ifdef VM
421 locate_block_device (BLOCK_SWAP, swap_bdev_name);
422#endif
423}
424
425/* Figures out what block device to use for the given ROLE: the
426 block device with the given NAME, if NAME is non-null,
427 otherwise the first block device in probe order of type
428 ROLE. */
429static void
430locate_block_device (enum block_type role, const char *name)
431{
432 struct block *block = NULL;
433
434 if (name != NULL)
435 {
436 block = block_get_by_name (name);
437 if (block == NULL)
438 PANIC ("No such block device \"%s\"", name);
439 }
440 else
441 {
442 for (block = block_first (); block != NULL; block = block_next (block))
443 if (block_type (block) == role)
444 break;
445 }
446
447 if (block != NULL)
448 {
449 printf ("%s: using %s\n", block_type_name (role), block_name (block));
450 block_set_role (role, block);
451 }
452}
453#endif
diff --git a/pintos-progos/threads/init.h b/pintos-progos/threads/init.h
new file mode 100644
index 0000000..8a3df90
--- /dev/null
+++ b/pintos-progos/threads/init.h
@@ -0,0 +1,12 @@
1#ifndef THREADS_INIT_H
2#define THREADS_INIT_H
3
4#include <debug.h>
5#include <stdbool.h>
6#include <stddef.h>
7#include <stdint.h>
8
9/* Page directory with kernel mappings only. */
10extern uint32_t *init_page_dir;
11
12#endif /* threads/init.h */
diff --git a/pintos-progos/threads/interrupt.c b/pintos-progos/threads/interrupt.c
new file mode 100644
index 0000000..e3b90dc
--- /dev/null
+++ b/pintos-progos/threads/interrupt.c
@@ -0,0 +1,438 @@
1#include "threads/interrupt.h"
2#include <debug.h>
3#include <inttypes.h>
4#include <stdint.h>
5#include <stdio.h>
6#include "threads/flags.h"
7#include "threads/intr-stubs.h"
8#include "threads/io.h"
9#include "threads/thread.h"
10#include "threads/vaddr.h"
11#include "devices/timer.h"
12
13/* Programmable Interrupt Controller (PIC) registers.
14 A PC has two PICs, called the master and slave PICs, with the
15 slave attached ("cascaded") to the master IRQ line 2. */
16#define PIC0_CTRL 0x20 /* Master PIC control register address. */
17#define PIC0_DATA 0x21 /* Master PIC data register address. */
18#define PIC1_CTRL 0xa0 /* Slave PIC control register address. */
19#define PIC1_DATA 0xa1 /* Slave PIC data register address. */
20
21/* Number of x86 interrupts. */
22#define INTR_CNT 256
23
24/* The Interrupt Descriptor Table (IDT). The format is fixed by
25 the CPU. See [IA32-v3a] sections 5.10 "Interrupt Descriptor
26 Table (IDT)", 5.11 "IDT Descriptors", 5.12.1.2 "Flag Usage By
27 Exception- or Interrupt-Handler Procedure". */
28static uint64_t idt[INTR_CNT];
29
30/* Interrupt handler functions for each interrupt. */
31static intr_handler_func *intr_handlers[INTR_CNT];
32
33/* Names for each interrupt, for debugging purposes. */
34static const char *intr_names[INTR_CNT];
35
36/* Number of unexpected interrupts for each vector. An
37 unexpected interrupt is one that has no registered handler. */
38static unsigned int unexpected_cnt[INTR_CNT];
39
40/* External interrupts are those generated by devices outside the
41 CPU, such as the timer. External interrupts run with
42 interrupts turned off, so they never nest, nor are they ever
43 pre-empted. Handlers for external interrupts also may not
44 sleep, although they may invoke intr_yield_on_return() to
45 request that a new process be scheduled just before the
46 interrupt returns. */
47static bool in_external_intr; /* Are we processing an external interrupt? */
48static bool yield_on_return; /* Should we yield on interrupt return? */
49
50/* Programmable Interrupt Controller helpers. */
51static void pic_init (void);
52static void pic_end_of_interrupt (int irq);
53
54/* Interrupt Descriptor Table helpers. */
55static uint64_t make_intr_gate (void (*) (void), int dpl);
56static uint64_t make_trap_gate (void (*) (void), int dpl);
57static inline uint64_t make_idtr_operand (uint16_t limit, void *base);
58
59/* Interrupt handlers. */
60void intr_handler (struct intr_frame *args);
61static void unexpected_interrupt (const struct intr_frame *);
62
63/* Returns the current interrupt status. */
64enum intr_level
65intr_get_level (void)
66{
67 uint32_t flags;
68
69 /* Push the flags register on the processor stack, then pop the
70 value off the stack into `flags'. See [IA32-v2b] "PUSHF"
71 and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware
72 Interrupts". */
73 asm volatile ("pushfl; popl %0" : "=g" (flags));
74
75 return flags & FLAG_IF ? INTR_ON : INTR_OFF;
76}
77
78/* Enables or disables interrupts as specified by LEVEL and
79 returns the previous interrupt status. */
80enum intr_level
81intr_set_level (enum intr_level level)
82{
83 return level == INTR_ON ? intr_enable () : intr_disable ();
84}
85
86/* Enables interrupts and returns the previous interrupt status. */
87enum intr_level
88intr_enable (void)
89{
90 enum intr_level old_level = intr_get_level ();
91 ASSERT (!intr_context ());
92
93 /* Enable interrupts by setting the interrupt flag.
94
95 See [IA32-v2b] "STI" and [IA32-v3a] 5.8.1 "Masking Maskable
96 Hardware Interrupts". */
97 asm volatile ("sti");
98
99 return old_level;
100}
101
102/* Disables interrupts and returns the previous interrupt status. */
103enum intr_level
104intr_disable (void)
105{
106 enum intr_level old_level = intr_get_level ();
107
108 /* Disable interrupts by clearing the interrupt flag.
109 See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable
110 Hardware Interrupts". */
111 asm volatile ("cli" : : : "memory");
112
113 return old_level;
114}
115
116/* Initializes the interrupt system. */
117void
118intr_init (void)
119{
120 uint64_t idtr_operand;
121 int i;
122
123 /* Initialize interrupt controller. */
124 pic_init ();
125
126 /* Initialize IDT. */
127 for (i = 0; i < INTR_CNT; i++)
128 idt[i] = make_intr_gate (intr_stubs[i], 0);
129
130 /* Load IDT register.
131 See [IA32-v2a] "LIDT" and [IA32-v3a] 5.10 "Interrupt
132 Descriptor Table (IDT)". */
133 idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
134 asm volatile ("lidt %0" : : "m" (idtr_operand));
135
136 /* Initialize intr_names. */
137 for (i = 0; i < INTR_CNT; i++)
138 intr_names[i] = "unknown";
139 intr_names[0] = "#DE Divide Error";
140 intr_names[1] = "#DB Debug Exception";
141 intr_names[2] = "NMI Interrupt";
142 intr_names[3] = "#BP Breakpoint Exception";
143 intr_names[4] = "#OF Overflow Exception";
144 intr_names[5] = "#BR BOUND Range Exceeded Exception";
145 intr_names[6] = "#UD Invalid Opcode Exception";
146 intr_names[7] = "#NM Device Not Available Exception";
147 intr_names[8] = "#DF Double Fault Exception";
148 intr_names[9] = "Coprocessor Segment Overrun";
149 intr_names[10] = "#TS Invalid TSS Exception";
150 intr_names[11] = "#NP Segment Not Present";
151 intr_names[12] = "#SS Stack Fault Exception";
152 intr_names[13] = "#GP General Protection Exception";
153 intr_names[14] = "#PF Page-Fault Exception";
154 intr_names[16] = "#MF x87 FPU Floating-Point Error";
155 intr_names[17] = "#AC Alignment Check Exception";
156 intr_names[18] = "#MC Machine-Check Exception";
157 intr_names[19] = "#XF SIMD Floating-Point Exception";
158}
159
160/* Registers interrupt VEC_NO to invoke HANDLER with descriptor
161 privilege level DPL. Names the interrupt NAME for debugging
162 purposes. The interrupt handler will be invoked with
163 interrupt status set to LEVEL. */
164static void
165register_handler (uint8_t vec_no, int dpl, enum intr_level level,
166 intr_handler_func *handler, const char *name)
167{
168 ASSERT (intr_handlers[vec_no] == NULL);
169 if (level == INTR_ON)
170 idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
171 else
172 idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
173 intr_handlers[vec_no] = handler;
174 intr_names[vec_no] = name;
175}
176
177/* Registers external interrupt VEC_NO to invoke HANDLER, which
178 is named NAME for debugging purposes. The handler will
179 execute with interrupts disabled. */
180void
181intr_register_ext (uint8_t vec_no, intr_handler_func *handler,
182 const char *name)
183{
184 ASSERT (vec_no >= 0x20 && vec_no <= 0x2f);
185 register_handler (vec_no, 0, INTR_OFF, handler, name);
186}
187
188/* Registers internal interrupt VEC_NO to invoke HANDLER, which
189 is named NAME for debugging purposes. The interrupt handler
190 will be invoked with interrupt status LEVEL.
191
192 The handler will have descriptor privilege level DPL, meaning
193 that it can be invoked intentionally when the processor is in
194 the DPL or lower-numbered ring. In practice, DPL==3 allows
195 user mode to invoke the interrupts and DPL==0 prevents such
196 invocation. Faults and exceptions that occur in user mode
197 still cause interrupts with DPL==0 to be invoked. See
198 [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1
199 "Accessing Nonconforming Code Segments" for further
200 discussion. */
201void
202intr_register_int (uint8_t vec_no, int dpl, enum intr_level level,
203 intr_handler_func *handler, const char *name)
204{
205 ASSERT (vec_no < 0x20 || vec_no > 0x2f);
206 register_handler (vec_no, dpl, level, handler, name);
207}
208
209/* Returns true during processing of an external interrupt
210 and false at all other times. */
211bool
212intr_context (void)
213{
214 return in_external_intr;
215}
216
217/* During processing of an external interrupt, directs the
218 interrupt handler to yield to a new process just before
219 returning from the interrupt. May not be called at any other
220 time. */
221void
222intr_yield_on_return (void)
223{
224 ASSERT (intr_context ());
225 yield_on_return = true;
226}
227
228/* 8259A Programmable Interrupt Controller. */
229
230/* Initializes the PICs. Refer to [8259A] for details.
231
232 By default, interrupts 0...15 delivered by the PICs will go to
233 interrupt vectors 0...15. Those vectors are also used for CPU
234 traps and exceptions, so we reprogram the PICs so that
235 interrupts 0...15 are delivered to interrupt vectors 32...47
236 (0x20...0x2f) instead. */
237static void
238pic_init (void)
239{
240 /* Mask all interrupts on both PICs. */
241 outb (PIC0_DATA, 0xff);
242 outb (PIC1_DATA, 0xff);
243
244 /* Initialize master. */
245 outb (PIC0_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
246 outb (PIC0_DATA, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
247 outb (PIC0_DATA, 0x04); /* ICW3: slave PIC on line IR2. */
248 outb (PIC0_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
249
250 /* Initialize slave. */
251 outb (PIC1_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
252 outb (PIC1_DATA, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
253 outb (PIC1_DATA, 0x02); /* ICW3: slave ID is 2. */
254 outb (PIC1_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
255
256 /* Unmask all interrupts. */
257 outb (PIC0_DATA, 0x00);
258 outb (PIC1_DATA, 0x00);
259}
260
261/* Sends an end-of-interrupt signal to the PIC for the given IRQ.
262 If we don't acknowledge the IRQ, it will never be delivered to
263 us again, so this is important. */
264static void
265pic_end_of_interrupt (int irq)
266{
267 ASSERT (irq >= 0x20 && irq < 0x30);
268
269 /* Acknowledge master PIC. */
270 outb (0x20, 0x20);
271
272 /* Acknowledge slave PIC if this is a slave interrupt. */
273 if (irq >= 0x28)
274 outb (0xa0, 0x20);
275}
276
277/* Creates an gate that invokes FUNCTION.
278
279 The gate has descriptor privilege level DPL, meaning that it
280 can be invoked intentionally when the processor is in the DPL
281 or lower-numbered ring. In practice, DPL==3 allows user mode
282 to call into the gate and DPL==0 prevents such calls. Faults
283 and exceptions that occur in user mode still cause gates with
284 DPL==0 to be invoked. See [IA32-v3a] sections 4.5 "Privilege
285 Levels" and 4.8.1.1 "Accessing Nonconforming Code Segments"
286 for further discussion.
287
288 TYPE must be either 14 (for an interrupt gate) or 15 (for a
289 trap gate). The difference is that entering an interrupt gate
290 disables interrupts, but entering a trap gate does not. See
291 [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or
292 Interrupt-Handler Procedure" for discussion. */
293static uint64_t
294make_gate (void (*function) (void), int dpl, int type)
295{
296 uint32_t e0, e1;
297
298 ASSERT (function != NULL);
299 ASSERT (dpl >= 0 && dpl <= 3);
300 ASSERT (type >= 0 && type <= 15);
301
302 e0 = (((uint32_t) function & 0xffff) /* Offset 15:0. */
303 | (SEL_KCSEG << 16)); /* Target code segment. */
304
305 e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
306 | (1 << 15) /* Present. */
307 | ((uint32_t) dpl << 13) /* Descriptor privilege level. */
308 | (0 << 12) /* System. */
309 | ((uint32_t) type << 8)); /* Gate type. */
310
311 return e0 | ((uint64_t) e1 << 32);
312}
313
314/* Creates an interrupt gate that invokes FUNCTION with the given
315 DPL. */
316static uint64_t
317make_intr_gate (void (*function) (void), int dpl)
318{
319 return make_gate (function, dpl, 14);
320}
321
322/* Creates a trap gate that invokes FUNCTION with the given
323 DPL. */
324static uint64_t
325make_trap_gate (void (*function) (void), int dpl)
326{
327 return make_gate (function, dpl, 15);
328}
329
330/* Returns a descriptor that yields the given LIMIT and BASE when
331 used as an operand for the LIDT instruction. */
332static inline uint64_t
333make_idtr_operand (uint16_t limit, void *base)
334{
335 return limit | ((uint64_t) (uint32_t) base << 16);
336}
337
338/* Interrupt handlers. */
339
340/* Handler for all interrupts, faults, and exceptions. This
341 function is called by the assembly language interrupt stubs in
342 intr-stubs.S. FRAME describes the interrupt and the
343 interrupted thread's registers. */
344void
345intr_handler (struct intr_frame *frame)
346{
347 bool external;
348 intr_handler_func *handler;
349
350 /* External interrupts are special.
351 We only handle one at a time (so interrupts must be off)
352 and they need to be acknowledged on the PIC (see below).
353 An external interrupt handler cannot sleep. */
354 external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
355 if (external)
356 {
357 ASSERT (intr_get_level () == INTR_OFF);
358 ASSERT (!intr_context ());
359
360 in_external_intr = true;
361 yield_on_return = false;
362 }
363
364 /* Invoke the interrupt's handler. */
365 handler = intr_handlers[frame->vec_no];
366 if (handler != NULL)
367 handler (frame);
368 else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f)
369 {
370 /* There is no handler, but this interrupt can trigger
371 spuriously due to a hardware fault or hardware race
372 condition. Ignore it. */
373 }
374 else
375 unexpected_interrupt (frame);
376
377 /* Complete the processing of an external interrupt. */
378 if (external)
379 {
380 ASSERT (intr_get_level () == INTR_OFF);
381 ASSERT (intr_context ());
382
383 in_external_intr = false;
384 pic_end_of_interrupt (frame->vec_no);
385
386 if (yield_on_return)
387 thread_yield ();
388 }
389}
390
391/* Handles an unexpected interrupt with interrupt frame F. An
392 unexpected interrupt is one that has no registered handler. */
393static void
394unexpected_interrupt (const struct intr_frame *f)
395{
396 /* Count the number so far. */
397 unsigned int n = ++unexpected_cnt[f->vec_no];
398
399 /* If the number is a power of 2, print a message. This rate
400 limiting means that we get information about an uncommon
401 unexpected interrupt the first time and fairly often after
402 that, but one that occurs many times will not overwhelm the
403 console. */
404 if ((n & (n - 1)) == 0)
405 printf ("Unexpected interrupt %#04x (%s)\n",
406 f->vec_no, intr_names[f->vec_no]);
407}
408
409/* Dumps interrupt frame F to the console, for debugging. */
410void
411intr_dump_frame (const struct intr_frame *f)
412{
413 uint32_t cr2;
414
415 /* Store current value of CR2 into `cr2'.
416 CR2 is the linear address of the last page fault.
417 See [IA32-v2a] "MOV--Move to/from Control Registers" and
418 [IA32-v3a] 5.14 "Interrupt 14--Page Fault Exception
419 (#PF)". */
420 asm ("movl %%cr2, %0" : "=r" (cr2));
421
422 printf ("Interrupt %#04x (%s) at eip=%p\n",
423 f->vec_no, intr_names[f->vec_no], f->eip);
424 printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
425 printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
426 f->eax, f->ebx, f->ecx, f->edx);
427 printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
428 f->esi, f->edi, (uint32_t) f->esp, f->ebp);
429 printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
430 f->cs, f->ds, f->es, f->ss);
431}
432
433/* Returns the name of interrupt VEC. */
434const char *
435intr_name (uint8_t vec)
436{
437 return intr_names[vec];
438}
diff --git a/pintos-progos/threads/interrupt.h b/pintos-progos/threads/interrupt.h
new file mode 100644
index 0000000..d43e06d
--- /dev/null
+++ b/pintos-progos/threads/interrupt.h
@@ -0,0 +1,70 @@
1#ifndef THREADS_INTERRUPT_H
2#define THREADS_INTERRUPT_H
3
4#include <stdbool.h>
5#include <stdint.h>
6
7/* Interrupts on or off? */
8enum intr_level
9 {
10 INTR_OFF, /* Interrupts disabled. */
11 INTR_ON /* Interrupts enabled. */
12 };
13
14enum intr_level intr_get_level (void);
15enum intr_level intr_set_level (enum intr_level);
16enum intr_level intr_enable (void);
17enum intr_level intr_disable (void);
18
19/* Interrupt stack frame. */
20struct intr_frame
21 {
22 /* Pushed by intr_entry in intr-stubs.S.
23 These are the interrupted task's saved registers. */
24 uint32_t edi; /* Saved EDI. */
25 uint32_t esi; /* Saved ESI. */
26 uint32_t ebp; /* Saved EBP. */
27 uint32_t esp_dummy; /* Not used. */
28 uint32_t ebx; /* Saved EBX. */
29 uint32_t edx; /* Saved EDX. */
30 uint32_t ecx; /* Saved ECX. */
31 uint32_t eax; /* Saved EAX. */
32 uint16_t gs, :16; /* Saved GS segment register. */
33 uint16_t fs, :16; /* Saved FS segment register. */
34 uint16_t es, :16; /* Saved ES segment register. */
35 uint16_t ds, :16; /* Saved DS segment register. */
36
37 /* Pushed by intrNN_stub in intr-stubs.S. */
38 uint32_t vec_no; /* Interrupt vector number. */
39
40 /* Sometimes pushed by the CPU,
41 otherwise for consistency pushed as 0 by intrNN_stub.
42 The CPU puts it just under `eip', but we move it here. */
43 uint32_t error_code; /* Error code. */
44
45 /* Pushed by intrNN_stub in intr-stubs.S.
46 This frame pointer eases interpretation of backtraces. */
47 void *frame_pointer; /* Saved EBP (frame pointer). */
48
49 /* Pushed by the CPU.
50 These are the interrupted task's saved registers. */
51 void (*eip) (void); /* Next instruction to execute. */
52 uint16_t cs, :16; /* Code segment for eip. */
53 uint32_t eflags; /* Saved CPU flags. */
54 void *esp; /* Saved stack pointer. */
55 uint16_t ss, :16; /* Data segment for esp. */
56 };
57
58typedef void intr_handler_func (struct intr_frame *);
59
60void intr_init (void);
61void intr_register_ext (uint8_t vec, intr_handler_func *, const char *name);
62void intr_register_int (uint8_t vec, int dpl, enum intr_level,
63 intr_handler_func *, const char *name);
64bool intr_context (void);
65void intr_yield_on_return (void);
66
67void intr_dump_frame (const struct intr_frame *);
68const char *intr_name (uint8_t vec);
69
70#endif /* threads/interrupt.h */
diff --git a/pintos-progos/threads/intr-stubs.S b/pintos-progos/threads/intr-stubs.S
new file mode 100644
index 0000000..adb674e
--- /dev/null
+++ b/pintos-progos/threads/intr-stubs.S
@@ -0,0 +1,203 @@
1#include "threads/loader.h"
2
3 .text
4
5/* Main interrupt entry point.
6
7 An internal or external interrupt starts in one of the
8 intrNN_stub routines, which push the `struct intr_frame'
9 frame_pointer, error_code, and vec_no members on the stack,
10 then jump here.
11
12 We save the rest of the `struct intr_frame' members to the
13 stack, set up some registers as needed by the kernel, and then
14 call intr_handler(), which actually handles the interrupt.
15
16 We "fall through" to intr_exit to return from the interrupt.
17*/
18.func intr_entry
19intr_entry:
20 /* Save caller's registers. */
21 pushl %ds
22 pushl %es
23 pushl %fs
24 pushl %gs
25 pushal
26
27 /* Set up kernel environment. */
28 cld /* String instructions go upward. */
29 mov $SEL_KDSEG, %eax /* Initialize segment registers. */
30 mov %eax, %ds
31 mov %eax, %es
32 leal 56(%esp), %ebp /* Set up frame pointer. */
33
34 /* Call interrupt handler. */
35 pushl %esp
36.globl intr_handler
37 call intr_handler
38 addl $4, %esp
39.endfunc
40
41/* Interrupt exit.
42
43 Restores the caller's registers, discards extra data on the
44 stack, and returns to the caller.
45
46 This is a separate function because it is called directly when
47 we launch a new user process (see start_process() in
48 userprog/process.c). */
49.globl intr_exit
50.func intr_exit
51intr_exit:
52 /* Restore caller's registers. */
53 popal
54 popl %gs
55 popl %fs
56 popl %es
57 popl %ds
58
59 /* Discard `struct intr_frame' vec_no, error_code,
60 frame_pointer members. */
61 addl $12, %esp
62
63 /* Return to caller. */
64 iret
65.endfunc
66
67/* Interrupt stubs.
68
69 This defines 256 fragments of code, named `intr00_stub'
70 through `intrff_stub', each of which is used as the entry
71 point for the corresponding interrupt vector. It also puts
72 the address of each of these functions in the correct spot in
73 `intr_stubs', an array of function pointers.
74
75 Most of the stubs do this:
76
77 1. Push %ebp on the stack (frame_pointer in `struct intr_frame').
78
79 2. Push 0 on the stack (error_code).
80
81 3. Push the interrupt number on the stack (vec_no).
82
83 The CPU pushes an extra "error code" on the stack for a few
84 interrupts. Because we want %ebp to be where the error code
85 is, we follow a different path:
86
87 1. Push a duplicate copy of the error code on the stack.
88
89 2. Replace the original copy of the error code by %ebp.
90
91 3. Push the interrupt number on the stack. */
92
93 .data
94.globl intr_stubs
95intr_stubs:
96
97/* This implements steps 1 and 2, described above, in the common
98 case where we just push a 0 error code. */
99#define zero \
100 pushl %ebp; \
101 pushl $0
102
103/* This implements steps 1 and 2, described above, in the case
104 where the CPU already pushed an error code. */
105#define REAL \
106 pushl (%esp); \
107 movl %ebp, 4(%esp)
108
109/* Emits a stub for interrupt vector NUMBER.
110 TYPE is `zero', for the case where we push a 0 error code,
111 or `REAL', if the CPU pushes an error code for us. */
112#define STUB(NUMBER, TYPE) \
113 .text; \
114.func intr##NUMBER##_stub; \
115intr##NUMBER##_stub: \
116 TYPE; \
117 push $0x##NUMBER; \
118 jmp intr_entry; \
119.endfunc; \
120 \
121 .data; \
122 .long intr##NUMBER##_stub;
123
124/* All the stubs. */
125STUB(00, zero) STUB(01, zero) STUB(02, zero) STUB(03, zero)
126STUB(04, zero) STUB(05, zero) STUB(06, zero) STUB(07, zero)
127STUB(08, REAL) STUB(09, zero) STUB(0a, REAL) STUB(0b, REAL)
128STUB(0c, zero) STUB(0d, REAL) STUB(0e, REAL) STUB(0f, zero)
129
130STUB(10, zero) STUB(11, REAL) STUB(12, zero) STUB(13, zero)
131STUB(14, zero) STUB(15, zero) STUB(16, zero) STUB(17, zero)
132STUB(18, REAL) STUB(19, zero) STUB(1a, REAL) STUB(1b, REAL)
133STUB(1c, zero) STUB(1d, REAL) STUB(1e, REAL) STUB(1f, zero)
134
135STUB(20, zero) STUB(21, zero) STUB(22, zero) STUB(23, zero)
136STUB(24, zero) STUB(25, zero) STUB(26, zero) STUB(27, zero)
137STUB(28, zero) STUB(29, zero) STUB(2a, zero) STUB(2b, zero)
138STUB(2c, zero) STUB(2d, zero) STUB(2e, zero) STUB(2f, zero)
139
140STUB(30, zero) STUB(31, zero) STUB(32, zero) STUB(33, zero)
141STUB(34, zero) STUB(35, zero) STUB(36, zero) STUB(37, zero)
142STUB(38, zero) STUB(39, zero) STUB(3a, zero) STUB(3b, zero)
143STUB(3c, zero) STUB(3d, zero) STUB(3e, zero) STUB(3f, zero)
144
145STUB(40, zero) STUB(41, zero) STUB(42, zero) STUB(43, zero)
146STUB(44, zero) STUB(45, zero) STUB(46, zero) STUB(47, zero)
147STUB(48, zero) STUB(49, zero) STUB(4a, zero) STUB(4b, zero)
148STUB(4c, zero) STUB(4d, zero) STUB(4e, zero) STUB(4f, zero)
149
150STUB(50, zero) STUB(51, zero) STUB(52, zero) STUB(53, zero)
151STUB(54, zero) STUB(55, zero) STUB(56, zero) STUB(57, zero)
152STUB(58, zero) STUB(59, zero) STUB(5a, zero) STUB(5b, zero)
153STUB(5c, zero) STUB(5d, zero) STUB(5e, zero) STUB(5f, zero)
154
155STUB(60, zero) STUB(61, zero) STUB(62, zero) STUB(63, zero)
156STUB(64, zero) STUB(65, zero) STUB(66, zero) STUB(67, zero)
157STUB(68, zero) STUB(69, zero) STUB(6a, zero) STUB(6b, zero)
158STUB(6c, zero) STUB(6d, zero) STUB(6e, zero) STUB(6f, zero)
159
160STUB(70, zero) STUB(71, zero) STUB(72, zero) STUB(73, zero)
161STUB(74, zero) STUB(75, zero) STUB(76, zero) STUB(77, zero)
162STUB(78, zero) STUB(79, zero) STUB(7a, zero) STUB(7b, zero)
163STUB(7c, zero) STUB(7d, zero) STUB(7e, zero) STUB(7f, zero)
164
165STUB(80, zero) STUB(81, zero) STUB(82, zero) STUB(83, zero)
166STUB(84, zero) STUB(85, zero) STUB(86, zero) STUB(87, zero)
167STUB(88, zero) STUB(89, zero) STUB(8a, zero) STUB(8b, zero)
168STUB(8c, zero) STUB(8d, zero) STUB(8e, zero) STUB(8f, zero)
169
170STUB(90, zero) STUB(91, zero) STUB(92, zero) STUB(93, zero)
171STUB(94, zero) STUB(95, zero) STUB(96, zero) STUB(97, zero)
172STUB(98, zero) STUB(99, zero) STUB(9a, zero) STUB(9b, zero)
173STUB(9c, zero) STUB(9d, zero) STUB(9e, zero) STUB(9f, zero)
174
175STUB(a0, zero) STUB(a1, zero) STUB(a2, zero) STUB(a3, zero)
176STUB(a4, zero) STUB(a5, zero) STUB(a6, zero) STUB(a7, zero)
177STUB(a8, zero) STUB(a9, zero) STUB(aa, zero) STUB(ab, zero)
178STUB(ac, zero) STUB(ad, zero) STUB(ae, zero) STUB(af, zero)
179
180STUB(b0, zero) STUB(b1, zero) STUB(b2, zero) STUB(b3, zero)
181STUB(b4, zero) STUB(b5, zero) STUB(b6, zero) STUB(b7, zero)
182STUB(b8, zero) STUB(b9, zero) STUB(ba, zero) STUB(bb, zero)
183STUB(bc, zero) STUB(bd, zero) STUB(be, zero) STUB(bf, zero)
184
185STUB(c0, zero) STUB(c1, zero) STUB(c2, zero) STUB(c3, zero)
186STUB(c4, zero) STUB(c5, zero) STUB(c6, zero) STUB(c7, zero)
187STUB(c8, zero) STUB(c9, zero) STUB(ca, zero) STUB(cb, zero)
188STUB(cc, zero) STUB(cd, zero) STUB(ce, zero) STUB(cf, zero)
189
190STUB(d0, zero) STUB(d1, zero) STUB(d2, zero) STUB(d3, zero)
191STUB(d4, zero) STUB(d5, zero) STUB(d6, zero) STUB(d7, zero)
192STUB(d8, zero) STUB(d9, zero) STUB(da, zero) STUB(db, zero)
193STUB(dc, zero) STUB(dd, zero) STUB(de, zero) STUB(df, zero)
194
195STUB(e0, zero) STUB(e1, zero) STUB(e2, zero) STUB(e3, zero)
196STUB(e4, zero) STUB(e5, zero) STUB(e6, zero) STUB(e7, zero)
197STUB(e8, zero) STUB(e9, zero) STUB(ea, zero) STUB(eb, zero)
198STUB(ec, zero) STUB(ed, zero) STUB(ee, zero) STUB(ef, zero)
199
200STUB(f0, zero) STUB(f1, zero) STUB(f2, zero) STUB(f3, zero)
201STUB(f4, zero) STUB(f5, zero) STUB(f6, zero) STUB(f7, zero)
202STUB(f8, zero) STUB(f9, zero) STUB(fa, zero) STUB(fb, zero)
203STUB(fc, zero) STUB(fd, zero) STUB(fe, zero) STUB(ff, zero)
diff --git a/pintos-progos/threads/intr-stubs.h b/pintos-progos/threads/intr-stubs.h
new file mode 100644
index 0000000..9ceba15
--- /dev/null
+++ b/pintos-progos/threads/intr-stubs.h
@@ -0,0 +1,19 @@
1#ifndef THREADS_INTR_STUBS_H
2#define THREADS_INTR_STUBS_H
3
4/* Interrupt stubs.
5
6 These are little snippets of code in intr-stubs.S, one for
7 each of the 256 possible x86 interrupts. Each one does a
8 little bit of stack manipulation, then jumps to intr_entry().
9 See intr-stubs.S for more information.
10
11 This array points to each of the interrupt stub entry points
12 so that intr_init() can easily find them. */
13typedef void intr_stub_func (void);
14extern intr_stub_func *intr_stubs[256];
15
16/* Interrupt return path. */
17void intr_exit (void);
18
19#endif /* threads/intr-stubs.h */
diff --git a/pintos-progos/threads/io.h b/pintos-progos/threads/io.h
new file mode 100644
index 0000000..30d52da
--- /dev/null
+++ b/pintos-progos/threads/io.h
@@ -0,0 +1,115 @@
1#ifndef THREADS_IO_H
2#define THREADS_IO_H
3
4#include <stddef.h>
5#include <stdint.h>
6
7/* Reads and returns a byte from PORT. */
8static inline uint8_t
9inb (uint16_t port)
10{
11 /* See [IA32-v2a] "IN". */
12 uint8_t data;
13 asm volatile ("inb %w1, %b0" : "=a" (data) : "Nd" (port));
14 return data;
15}
16
17/* Reads CNT bytes from PORT, one after another, and stores them
18 into the buffer starting at ADDR. */
19static inline void
20insb (uint16_t port, void *addr, size_t cnt)
21{
22 /* See [IA32-v2a] "INS". */
23 asm volatile ("rep insb" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory");
24}
25
26/* Reads and returns 16 bits from PORT. */
27static inline uint16_t
28inw (uint16_t port)
29{
30 uint16_t data;
31 /* See [IA32-v2a] "IN". */
32 asm volatile ("inw %w1, %w0" : "=a" (data) : "Nd" (port));
33 return data;
34}
35
36/* Reads CNT 16-bit (halfword) units from PORT, one after
37 another, and stores them into the buffer starting at ADDR. */
38static inline void
39insw (uint16_t port, void *addr, size_t cnt)
40{
41 /* See [IA32-v2a] "INS". */
42 asm volatile ("rep insw" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory");
43}
44
45/* Reads and returns 32 bits from PORT. */
46static inline uint32_t
47inl (uint16_t port)
48{
49 /* See [IA32-v2a] "IN". */
50 uint32_t data;
51 asm volatile ("inl %w1, %0" : "=a" (data) : "Nd" (port));
52 return data;
53}
54
55/* Reads CNT 32-bit (word) units from PORT, one after another,
56 and stores them into the buffer starting at ADDR. */
57static inline void
58insl (uint16_t port, void *addr, size_t cnt)
59{
60 /* See [IA32-v2a] "INS". */
61 asm volatile ("rep insl" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory");
62}
63
64/* Writes byte DATA to PORT. */
65static inline void
66outb (uint16_t port, uint8_t data)
67{
68 /* See [IA32-v2b] "OUT". */
69 asm volatile ("outb %b0, %w1" : : "a" (data), "Nd" (port));
70}
71
72/* Writes to PORT each byte of data in the CNT-byte buffer
73 starting at ADDR. */
74static inline void
75outsb (uint16_t port, const void *addr, size_t cnt)
76{
77 /* See [IA32-v2b] "OUTS". */
78 asm volatile ("rep outsb" : "+S" (addr), "+c" (cnt) : "d" (port));
79}
80
81/* Writes the 16-bit DATA to PORT. */
82static inline void
83outw (uint16_t port, uint16_t data)
84{
85 /* See [IA32-v2b] "OUT". */
86 asm volatile ("outw %w0, %w1" : : "a" (data), "Nd" (port));
87}
88
89/* Writes to PORT each 16-bit unit (halfword) of data in the
90 CNT-halfword buffer starting at ADDR. */
91static inline void
92outsw (uint16_t port, const void *addr, size_t cnt)
93{
94 /* See [IA32-v2b] "OUTS". */
95 asm volatile ("rep outsw" : "+S" (addr), "+c" (cnt) : "d" (port));
96}
97
98/* Writes the 32-bit DATA to PORT. */
99static inline void
100outl (uint16_t port, uint32_t data)
101{
102 /* See [IA32-v2b] "OUT". */
103 asm volatile ("outl %0, %w1" : : "a" (data), "Nd" (port));
104}
105
106/* Writes to PORT each 32-bit unit (word) of data in the CNT-word
107 buffer starting at ADDR. */
108static inline void
109outsl (uint16_t port, const void *addr, size_t cnt)
110{
111 /* See [IA32-v2b] "OUTS". */
112 asm volatile ("rep outsl" : "+S" (addr), "+c" (cnt) : "d" (port));
113}
114
115#endif /* threads/io.h */
diff --git a/pintos-progos/threads/kernel.lds.S b/pintos-progos/threads/kernel.lds.S
new file mode 100644
index 0000000..19082d5
--- /dev/null
+++ b/pintos-progos/threads/kernel.lds.S
@@ -0,0 +1,30 @@
1#include "threads/loader.h"
2
3OUTPUT_FORMAT("elf32-i386")
4OUTPUT_ARCH("i386")
5ENTRY(start) /* Kernel starts at "start" symbol. */
6SECTIONS
7{
8 /* Specify the kernel base address. */
9 _start = LOADER_PHYS_BASE + LOADER_KERN_BASE;
10
11 /* Make room for the ELF headers. */
12 . = _start + SIZEOF_HEADERS;
13
14 /* Kernel starts with code, followed by read-only data and writable data. */
15 .text : { *(.start) *(.text) } = 0x90
16 .rodata : { *(.rodata) *(.rodata.*)
17 . = ALIGN(0x1000);
18 _end_kernel_text = .; }
19 .data : { *(.data)
20 _signature = .; LONG(0xaa55aa55) }
21
22 /* BSS (zero-initialized data) is after everything else. */
23 _start_bss = .;
24 .bss : { *(.bss) }
25 _end_bss = .;
26
27 _end = .;
28
29 ASSERT (_end - _start <= 512K, "Kernel image is too big.")
30}
diff --git a/pintos-progos/threads/loader.S b/pintos-progos/threads/loader.S
new file mode 100644
index 0000000..dd87ea1
--- /dev/null
+++ b/pintos-progos/threads/loader.S
@@ -0,0 +1,263 @@
1#include "threads/loader.h"
2
3#### Kernel loader.
4
5#### This code should be stored in the first sector of a hard disk.
6#### When the BIOS runs, it loads this code at physical address
7#### 0x7c00-0x7e00 (512 bytes) and jumps to the beginning of it,
8#### in real mode. The loader loads the kernel into memory and jumps
9#### to its entry point, which is the start function in start.S.
10####
11#### The BIOS passes in the drive that the loader was read from as
12#### DL, with floppy drives numbered 0x00, 0x01, ... and hard drives
13#### numbered 0x80, 0x81, ... We want to support booting a kernel on
14#### a different drive from the loader, so we don't take advantage of
15#### this.
16
17# Runs in real mode, which is a 16-bit segment.
18 .code16
19
20# Set up segment registers.
21# Set stack to grow downward from 60 kB (after boot, the kernel
22# continues to use this stack for its initial thread).
23
24 sub %ax, %ax
25 mov %ax, %ds
26 mov %ax, %ss
27 mov $0xf000, %esp
28
29# Configure serial port so we can report progress without connected VGA.
30# See [IntrList] for details.
31 sub %dx, %dx # Serial port 0.
32 mov $0xe3, %al # 9600 bps, N-8-1.
33 # AH is already 0 (Initialize Port).
34 int $0x14 # Destroys AX.
35
36 call puts
37 .string "PiLo"
38
39#### Read the partition table on each system hard disk and scan for a
40#### partition of type 0x20, which is the type that we use for a
41#### Pintos kernel.
42####
43#### Read [Partitions] for a description of the partition table format
44#### that we parse.
45####
46#### We print out status messages to show the disk and partition being
47#### scanned, e.g. hda1234 as we scan four partitions on the first
48#### hard disk.
49
50 mov $0x80, %dl # Hard disk 0.
51read_mbr:
52 sub %ebx, %ebx # Sector 0.
53 mov $0x2000, %ax # Use 0x20000 for buffer.
54 mov %ax, %es
55 call read_sector
56 jc no_such_drive
57
58 # Print hd[a-z].
59 call puts
60 .string " hd"
61 mov %dl, %al
62 add $'a' - 0x80, %al
63 call putc
64
65 # Check for MBR signature--if not present, it's not a
66 # partitioned hard disk.
67 cmpw $0xaa55, %es:510
68 jne next_drive
69
70 mov $446, %si # Offset of partition table entry 1.
71 mov $'1', %al
72check_partition:
73 # Is it an unused partition?
74 cmpl $0, %es:(%si)
75 je next_partition
76
77 # Print [1-4].
78 call putc
79
80 # Is it a Pintos kernel partition?
81 cmpb $0x20, %es:4(%si)
82 jne next_partition
83
84 # Is it a bootable partition?
85 cmpb $0x80, %es:(%si)
86 je load_kernel
87
88next_partition:
89 # No match for this partition, go on to the next one.
90 add $16, %si # Offset to next partition table entry.
91 inc %al
92 cmp $510, %si
93 jb check_partition
94
95next_drive:
96 # No match on this drive, go on to the next one.
97 inc %dl
98 jnc read_mbr
99
100no_such_drive:
101no_boot_partition:
102 # Didn't find a Pintos kernel partition anywhere, give up.
103 call puts
104 .string "\rNot found\r"
105
106 # Notify BIOS that boot failed. See [IntrList].
107 int $0x18
108
109#### We found a kernel. The kernel's drive is in DL. The partition
110#### table entry for the kernel's partition is at ES:SI. Our job now
111#### is to read the kernel from disk and jump to its start address.
112
113load_kernel:
114 call puts
115 .string "\rLoading"
116
117 # Figure out number of sectors to read. A Pintos kernel is
118 # just an ELF format object, which doesn't have an
119 # easy-to-read field to identify its own size (see [ELF1]).
120 # But we limit Pintos kernels to 512 kB for other reasons, so
121 # it's easy enough to just read the entire contents of the
122 # partition or 512 kB from disk, whichever is smaller.
123 mov %es:12(%si), %ecx # EBP = number of sectors
124 cmp $1024, %ecx # Cap size at 512 kB
125 jbe 1f
126 mov $1024, %cx
1271:
128
129 mov %es:8(%si), %ebx # EBX = first sector
130 mov $0x2000, %ax # Start load address: 0x20000
131
132next_sector:
133 # Read one sector into memory.
134 mov %ax, %es # ES:0000 -> load address
135 call read_sector
136 jc read_failed
137
138 # Print '.' as progress indicator once every 16 sectors == 8 kB.
139 test $15, %bl
140 jnz 1f
141 call puts
142 .string "."
1431:
144
145 # Advance memory pointer and disk sector.
146 add $0x20, %ax
147 inc %bx
148 loop next_sector
149
150 call puts
151 .string "\r"
152
153#### Transfer control to the kernel that we loaded. We read the start
154#### address out of the ELF header (see [ELF1]) and convert it from a
155#### 32-bit linear address into a 16:16 segment:offset address for
156#### real mode, then jump to the converted address. The 80x86 doesn't
157#### have an instruction to jump to an absolute segment:offset kept in
158#### registers, so in fact we store the address in a temporary memory
159#### location, then jump indirectly through that location. To save 4
160#### bytes in the loader, we reuse 4 bytes of the loader's code for
161#### this temporary pointer.
162
163 mov $0x2000, %ax
164 mov %ax, %es
165 mov %es:0x18, %dx
166 mov %dx, start
167 movw $0x2000, start + 2
168 ljmp *start
169
170read_failed:
171start:
172 # Disk sector read failed.
173 call puts
1741: .string "\rBad read\r"
175
176 # Notify BIOS that boot failed. See [IntrList].
177 int $0x18
178
179#### Print string subroutine. To save space in the loader, this
180#### subroutine takes its null-terminated string argument from the
181#### code stream just after the call, and then returns to the byte
182#### just after the terminating null. This subroutine preserves all
183#### general-purpose registers.
184
185puts: xchg %si, %ss:(%esp)
186 push %ax
187next_char:
188 mov %cs:(%si), %al
189 inc %si
190 test %al, %al
191 jz 1f
192 call putc
193 jmp next_char
1941: pop %ax
195 xchg %si, %ss:(%esp)
196 ret
197
198#### Character output subroutine. Prints the character in AL to the
199#### VGA display and serial port 0, using BIOS services (see
200#### [IntrList]). Preserves all general-purpose registers.
201####
202#### If called upon to output a carriage return, this subroutine
203#### automatically supplies the following line feed.
204
205putc: pusha
206
2071: sub %bh, %bh # Page 0.
208 mov $0x0e, %ah # Teletype output service.
209 int $0x10
210
211 mov $0x01, %ah # Serial port output service.
212 sub %dx, %dx # Serial port 0.
2132: int $0x14 # Destroys AH.
214 test $0x80, %ah # Output timed out?
215 jz 3f
216 movw $0x9090, 2b # Turn "int $0x14" above into NOPs.
217
2183:
219 cmp $'\r', %al
220 jne popa_ret
221 mov $'\n', %al
222 jmp 1b
223
224#### Sector read subroutine. Takes a drive number in DL (0x80 = hard
225#### disk 0, 0x81 = hard disk 1, ...) and a sector number in EBX, and
226#### reads the specified sector into memory at ES:0000. Returns with
227#### carry set on error, clear otherwise. Preserves all
228#### general-purpose registers.
229
230read_sector:
231 pusha
232 sub %ax, %ax
233 push %ax # LBA sector number [48:63]
234 push %ax # LBA sector number [32:47]
235 push %ebx # LBA sector number [0:31]
236 push %es # Buffer segment
237 push %ax # Buffer offset (always 0)
238 push $1 # Number of sectors to read
239 push $16 # Packet size
240 mov $0x42, %ah # Extended read
241 mov %sp, %si # DS:SI -> packet
242 int $0x13 # Error code in CF
243 popa # Pop 16 bytes, preserve flags
244popa_ret:
245 popa
246 ret # Error code still in CF
247
248#### Command-line arguments and their count.
249#### This is written by the `pintos' utility and read by the kernel.
250#### The loader itself does not do anything with the command line.
251 .org LOADER_ARG_CNT - LOADER_BASE
252 .fill LOADER_ARG_CNT_LEN, 1, 0
253
254 .org LOADER_ARGS - LOADER_BASE
255 .fill LOADER_ARGS_LEN, 1, 0
256
257#### Partition table.
258 .org LOADER_PARTS - LOADER_BASE
259 .fill LOADER_PARTS_LEN, 1, 0
260
261#### Boot-sector signature for BIOS inspection.
262 .org LOADER_SIG - LOADER_BASE
263 .word 0xaa55
diff --git a/pintos-progos/threads/loader.h b/pintos-progos/threads/loader.h
new file mode 100644
index 0000000..1bfe111
--- /dev/null
+++ b/pintos-progos/threads/loader.h
@@ -0,0 +1,40 @@
1#ifndef THREADS_LOADER_H
2#define THREADS_LOADER_H
3
4/* Constants fixed by the PC BIOS. */
5#define LOADER_BASE 0x7c00 /* Physical address of loader's base. */
6#define LOADER_END 0x7e00 /* Physical address of end of loader. */
7
8/* Physical address of kernel base. */
9#define LOADER_KERN_BASE 0x20000 /* 128 kB. */
10
11/* Kernel virtual address at which all physical memory is mapped.
12 Must be aligned on a 4 MB boundary. */
13#define LOADER_PHYS_BASE 0xc0000000 /* 3 GB. */
14
15/* Important loader physical addresses. */
16#define LOADER_SIG (LOADER_END - LOADER_SIG_LEN) /* 0xaa55 BIOS signature. */
17#define LOADER_PARTS (LOADER_SIG - LOADER_PARTS_LEN) /* Partition table. */
18#define LOADER_ARGS (LOADER_PARTS - LOADER_ARGS_LEN) /* Command-line args. */
19#define LOADER_ARG_CNT (LOADER_ARGS - LOADER_ARG_CNT_LEN) /* Number of args. */
20
21/* Sizes of loader data structures. */
22#define LOADER_SIG_LEN 2
23#define LOADER_PARTS_LEN 64
24#define LOADER_ARGS_LEN 128
25#define LOADER_ARG_CNT_LEN 4
26
27/* GDT selectors defined by loader.
28 More selectors are defined by userprog/gdt.h. */
29#define SEL_NULL 0x00 /* Null selector. */
30#define SEL_KCSEG 0x08 /* Kernel code selector. */
31#define SEL_KDSEG 0x10 /* Kernel data selector. */
32
33#ifndef __ASSEMBLER__
34#include <stdint.h>
35
36/* Amount of physical memory, in 4 kB pages. */
37extern uint32_t init_ram_pages;
38#endif
39
40#endif /* threads/loader.h */
diff --git a/pintos-progos/threads/malloc.c b/pintos-progos/threads/malloc.c
new file mode 100644
index 0000000..f6f803b
--- /dev/null
+++ b/pintos-progos/threads/malloc.c
@@ -0,0 +1,294 @@
1#include "threads/malloc.h"
2#include <debug.h>
3#include <list.h>
4#include <round.h>
5#include <stdint.h>
6#include <stdio.h>
7#include <string.h>
8#include "threads/palloc.h"
9#include "threads/synch.h"
10#include "threads/vaddr.h"
11
12/* A simple implementation of malloc().
13
14 The size of each request, in bytes, is rounded up to a power
15 of 2 and assigned to the "descriptor" that manages blocks of
16 that size. The descriptor keeps a list of free blocks. If
17 the free list is nonempty, one of its blocks is used to
18 satisfy the request.
19
20 Otherwise, a new page of memory, called an "arena", is
21 obtained from the page allocator (if none is available,
22 malloc() returns a null pointer). The new arena is divided
23 into blocks, all of which are added to the descriptor's free
24 list. Then we return one of the new blocks.
25
26 When we free a block, we add it to its descriptor's free list.
27 But if the arena that the block was in now has no in-use
28 blocks, we remove all of the arena's blocks from the free list
29 and give the arena back to the page allocator.
30
31 We can't handle blocks bigger than 2 kB using this scheme,
32 because they're too big to fit in a single page with a
33 descriptor. We handle those by allocating contiguous pages
34 with the page allocator and sticking the allocation size at
35 the beginning of the allocated block's arena header. */
36
37/* Descriptor. */
38struct desc
39 {
40 size_t block_size; /* Size of each element in bytes. */
41 size_t blocks_per_arena; /* Number of blocks in an arena. */
42 struct list free_list; /* List of free blocks. */
43 struct lock lock; /* Lock. */
44 };
45
46/* Magic number for detecting arena corruption. */
47#define ARENA_MAGIC 0x9a548eed
48
49/* Arena. */
50struct arena
51 {
52 unsigned magic; /* Always set to ARENA_MAGIC. */
53 struct desc *desc; /* Owning descriptor, null for big block. */
54 size_t free_cnt; /* Free blocks; pages in big block. */
55 };
56
57/* Free block. */
58struct block
59 {
60 struct list_elem free_elem; /* Free list element. */
61 };
62
63/* Our set of descriptors. */
64static struct desc descs[10]; /* Descriptors. */
65static size_t desc_cnt; /* Number of descriptors. */
66
67static struct arena *block_to_arena (struct block *);
68static struct block *arena_to_block (struct arena *, size_t idx);
69
70/* Initializes the malloc() descriptors. */
71void
72malloc_init (void)
73{
74 size_t block_size;
75
76 for (block_size = 16; block_size < PGSIZE / 2; block_size *= 2)
77 {
78 struct desc *d = &descs[desc_cnt++];
79 ASSERT (desc_cnt <= sizeof descs / sizeof *descs);
80 d->block_size = block_size;
81 d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size;
82 list_init (&d->free_list);
83 lock_init (&d->lock);
84 }
85}
86
87/* Obtains and returns a new block of at least SIZE bytes.
88 Returns a null pointer if memory is not available. */
89void *
90malloc (size_t size)
91{
92 struct desc *d;
93 struct block *b;
94 struct arena *a;
95
96 /* A null pointer satisfies a request for 0 bytes. */
97 if (size == 0)
98 return NULL;
99
100 /* Find the smallest descriptor that satisfies a SIZE-byte
101 request. */
102 for (d = descs; d < descs + desc_cnt; d++)
103 if (d->block_size >= size)
104 break;
105 if (d == descs + desc_cnt)
106 {
107 /* SIZE is too big for any descriptor.
108 Allocate enough pages to hold SIZE plus an arena. */
109 size_t page_cnt = DIV_ROUND_UP (size + sizeof *a, PGSIZE);
110 a = palloc_get_multiple (0, page_cnt);
111 if (a == NULL)
112 return NULL;
113
114 /* Initialize the arena to indicate a big block of PAGE_CNT
115 pages, and return it. */
116 a->magic = ARENA_MAGIC;
117 a->desc = NULL;
118 a->free_cnt = page_cnt;
119 return a + 1;
120 }
121
122 lock_acquire (&d->lock);
123
124 /* If the free list is empty, create a new arena. */
125 if (list_empty (&d->free_list))
126 {
127 size_t i;
128
129 /* Allocate a page. */
130 a = palloc_get_page (0);
131 if (a == NULL)
132 {
133 lock_release (&d->lock);
134 return NULL;
135 }
136
137 /* Initialize arena and add its blocks to the free list. */
138 a->magic = ARENA_MAGIC;
139 a->desc = d;
140 a->free_cnt = d->blocks_per_arena;
141 for (i = 0; i < d->blocks_per_arena; i++)
142 {
143 struct block *b = arena_to_block (a, i);
144 list_push_back (&d->free_list, &b->free_elem);
145 }
146 }
147
148 /* Get a block from free list and return it. */
149 b = list_entry (list_pop_front (&d->free_list), struct block, free_elem);
150 a = block_to_arena (b);
151 a->free_cnt--;
152 lock_release (&d->lock);
153 return b;
154}
155
156/* Allocates and return A times B bytes initialized to zeroes.
157 Returns a null pointer if memory is not available. */
158void *
159calloc (size_t a, size_t b)
160{
161 void *p;
162 size_t size;
163
164 /* Calculate block size and make sure it fits in size_t. */
165 size = a * b;
166 if (size < a || size < b)
167 return NULL;
168
169 /* Allocate and zero memory. */
170 p = malloc (size);
171 if (p != NULL)
172 memset (p, 0, size);
173
174 return p;
175}
176
177/* Returns the number of bytes allocated for BLOCK. */
178static size_t
179block_size (void *block)
180{
181 struct block *b = block;
182 struct arena *a = block_to_arena (b);
183 struct desc *d = a->desc;
184
185 return d != NULL ? d->block_size : PGSIZE * a->free_cnt - pg_ofs (block);
186}
187
188/* Attempts to resize OLD_BLOCK to NEW_SIZE bytes, possibly
189 moving it in the process.
190 If successful, returns the new block; on failure, returns a
191 null pointer.
192 A call with null OLD_BLOCK is equivalent to malloc(NEW_SIZE).
193 A call with zero NEW_SIZE is equivalent to free(OLD_BLOCK). */
194void *
195realloc (void *old_block, size_t new_size)
196{
197 if (new_size == 0)
198 {
199 free (old_block);
200 return NULL;
201 }
202 else
203 {
204 void *new_block = malloc (new_size);
205 if (old_block != NULL && new_block != NULL)
206 {
207 size_t old_size = block_size (old_block);
208 size_t min_size = new_size < old_size ? new_size : old_size;
209 memcpy (new_block, old_block, min_size);
210 free (old_block);
211 }
212 return new_block;
213 }
214}
215
216/* Frees block P, which must have been previously allocated with
217 malloc(), calloc(), or realloc(). */
218void
219free (void *p)
220{
221 if (p != NULL)
222 {
223 struct block *b = p;
224 struct arena *a = block_to_arena (b);
225 struct desc *d = a->desc;
226
227 if (d != NULL)
228 {
229 /* It's a normal block. We handle it here. */
230
231#ifndef NDEBUG
232 /* Clear the block to help detect use-after-free bugs. */
233 memset (b, 0xcc, d->block_size);
234#endif
235
236 lock_acquire (&d->lock);
237
238 /* Add block to free list. */
239 list_push_front (&d->free_list, &b->free_elem);
240
241 /* If the arena is now entirely unused, free it. */
242 if (++a->free_cnt >= d->blocks_per_arena)
243 {
244 size_t i;
245
246 ASSERT (a->free_cnt == d->blocks_per_arena);
247 for (i = 0; i < d->blocks_per_arena; i++)
248 {
249 struct block *b = arena_to_block (a, i);
250 list_remove (&b->free_elem);
251 }
252 palloc_free_page (a);
253 }
254
255 lock_release (&d->lock);
256 }
257 else
258 {
259 /* It's a big block. Free its pages. */
260 palloc_free_multiple (a, a->free_cnt);
261 return;
262 }
263 }
264}
265
266/* Returns the arena that block B is inside. */
267static struct arena *
268block_to_arena (struct block *b)
269{
270 struct arena *a = pg_round_down (b);
271
272 /* Check that the arena is valid. */
273 ASSERT (a != NULL);
274 ASSERT (a->magic == ARENA_MAGIC);
275
276 /* Check that the block is properly aligned for the arena. */
277 ASSERT (a->desc == NULL
278 || (pg_ofs (b) - sizeof *a) % a->desc->block_size == 0);
279 ASSERT (a->desc != NULL || pg_ofs (b) == sizeof *a);
280
281 return a;
282}
283
284/* Returns the (IDX - 1)'th block within arena A. */
285static struct block *
286arena_to_block (struct arena *a, size_t idx)
287{
288 ASSERT (a != NULL);
289 ASSERT (a->magic == ARENA_MAGIC);
290 ASSERT (idx < a->desc->blocks_per_arena);
291 return (struct block *) ((uint8_t *) a
292 + sizeof *a
293 + idx * a->desc->block_size);
294}
diff --git a/pintos-progos/threads/malloc.h b/pintos-progos/threads/malloc.h
new file mode 100644
index 0000000..bc55d36
--- /dev/null
+++ b/pintos-progos/threads/malloc.h
@@ -0,0 +1,13 @@
1#ifndef THREADS_MALLOC_H
2#define THREADS_MALLOC_H
3
4#include <debug.h>
5#include <stddef.h>
6
7void malloc_init (void);
8void *malloc (size_t) __attribute__ ((malloc));
9void *calloc (size_t, size_t) __attribute__ ((malloc));
10void *realloc (void *, size_t);
11void free (void *);
12
13#endif /* threads/malloc.h */
diff --git a/pintos-progos/threads/palloc.c b/pintos-progos/threads/palloc.c
new file mode 100644
index 0000000..5c7ef2f
--- /dev/null
+++ b/pintos-progos/threads/palloc.c
@@ -0,0 +1,199 @@
1#include "threads/palloc.h"
2#include <bitmap.h>
3#include <debug.h>
4#include <inttypes.h>
5#include <round.h>
6#include <stddef.h>
7#include <stdint.h>
8#include <stdio.h>
9#include <string.h>
10#include "threads/loader.h"
11#include "threads/synch.h"
12#include "threads/vaddr.h"
13
14/* Page allocator. Hands out memory in page-size (or
15 page-multiple) chunks. See malloc.h for an allocator that
16 hands out smaller chunks.
17
18 System memory is divided into two "pools" called the kernel
19 and user pools. The user pool is for user (virtual) memory
20 pages, the kernel pool for everything else. The idea here is
21 that the kernel needs to have memory for its own operations
22 even if user processes are swapping like mad.
23
24 By default, half of system RAM is given to the kernel pool and
25 half to the user pool. That should be huge overkill for the
26 kernel pool, but that's just fine for demonstration purposes. */
27
28/* A memory pool. */
29struct pool
30 {
31 struct lock lock; /* Mutual exclusion. */
32 struct bitmap *used_map; /* Bitmap of free pages. */
33 uint8_t *base; /* Base of pool. */
34 };
35
36/* Two pools: one for kernel data, one for user pages. */
37static struct pool kernel_pool, user_pool;
38
39static void init_pool (struct pool *, void *base, size_t page_cnt,
40 const char *name);
41static bool page_from_pool (const struct pool *, void *page);
42
43/* Initializes the page allocator. At most USER_PAGE_LIMIT
44 pages are put into the user pool. */
45void
46palloc_init (size_t user_page_limit)
47{
48 /* Free memory starts at 1 MB and runs to the end of RAM. */
49 uint8_t *free_start = ptov (1024 * 1024);
50 uint8_t *free_end = ptov (init_ram_pages * PGSIZE);
51 size_t free_pages = (free_end - free_start) / PGSIZE;
52 size_t user_pages = free_pages / 2;
53 size_t kernel_pages;
54 if (user_pages > user_page_limit)
55 user_pages = user_page_limit;
56 kernel_pages = free_pages - user_pages;
57
58 /* Give half of memory to kernel, half to user. */
59 init_pool (&kernel_pool, free_start, kernel_pages, "kernel pool");
60 init_pool (&user_pool, free_start + kernel_pages * PGSIZE,
61 user_pages, "user pool");
62}
63
64/* Obtains and returns a group of PAGE_CNT contiguous free pages.
65 If PAL_USER is set, the pages are obtained from the user pool,
66 otherwise from the kernel pool. If PAL_ZERO is set in FLAGS,
67 then the pages are filled with zeros. If too few pages are
68 available, returns a null pointer, unless PAL_ASSERT is set in
69 FLAGS, in which case the kernel panics. */
70void *
71palloc_get_multiple (enum palloc_flags flags, size_t page_cnt)
72{
73 struct pool *pool = flags & PAL_USER ? &user_pool : &kernel_pool;
74 void *pages;
75 size_t page_idx;
76
77 if (page_cnt == 0)
78 return NULL;
79
80 lock_acquire (&pool->lock);
81 page_idx = bitmap_scan_and_flip (pool->used_map, 0, page_cnt, false);
82 lock_release (&pool->lock);
83
84 if (page_idx != BITMAP_ERROR)
85 pages = pool->base + PGSIZE * page_idx;
86 else
87 pages = NULL;
88
89 if (pages != NULL)
90 {
91 if (flags & PAL_ZERO)
92 memset (pages, 0, PGSIZE * page_cnt);
93 }
94 else
95 {
96 if (flags & PAL_ASSERT)
97 PANIC ("palloc_get: out of pages");
98 }
99
100 return pages;
101}
102
103/* Obtains a single free page and returns its kernel virtual
104 address.
105 If PAL_USER is set, the page is obtained from the user pool,
106 otherwise from the kernel pool. If PAL_ZERO is set in FLAGS,
107 then the page is filled with zeros. If no pages are
108 available, returns a null pointer, unless PAL_ASSERT is set in
109 FLAGS, in which case the kernel panics. */
110void *
111palloc_get_page (enum palloc_flags flags)
112{
113 return palloc_get_multiple (flags, 1);
114}
115
116/* Frees the PAGE_CNT pages starting at PAGES. */
117void
118palloc_free_multiple (void *pages, size_t page_cnt)
119{
120 struct pool *pool;
121 size_t page_idx;
122
123 ASSERT (pg_ofs (pages) == 0);
124 if (pages == NULL || page_cnt == 0)
125 return;
126
127 if (page_from_pool (&kernel_pool, pages))
128 pool = &kernel_pool;
129 else if (page_from_pool (&user_pool, pages))
130 pool = &user_pool;
131 else
132 NOT_REACHED ();
133
134 page_idx = pg_no (pages) - pg_no (pool->base);
135
136#ifndef NDEBUG
137 memset (pages, 0xcc, PGSIZE * page_cnt);
138#endif
139
140 ASSERT (bitmap_all (pool->used_map, page_idx, page_cnt));
141 bitmap_set_multiple (pool->used_map, page_idx, page_cnt, false);
142}
143
144/* Frees the page at PAGE. */
145void
146palloc_free_page (void *page)
147{
148 palloc_free_multiple (page, 1);
149}
150
151/* List all used pages */
152void
153palloc_dump_used_pages ()
154{
155 struct pool *pool;
156 int pool_index;
157 for (pool_index = 0; pool_index < 2; pool_index++) {
158 pool = (pool_index == 0) ? &kernel_pool : &user_pool;
159 printf("%s Pool at %p\n", (pool_index == 0) ? "kernel" : "user", pool->base);
160 size_t start = 0, index;
161 while ((index = bitmap_scan(pool->used_map,start,1,true)) != BITMAP_ERROR) {
162 printf(" - %p\n",pool->base + (PGSIZE * index));
163 start = index + 1;
164 }
165 }
166}
167
168/* Initializes pool P as starting at START and ending at END,
169 naming it NAME for debugging purposes. */
170static void
171init_pool (struct pool *p, void *base, size_t page_cnt, const char *name)
172{
173 /* We'll put the pool's used_map at its base.
174 Calculate the space needed for the bitmap
175 and subtract it from the pool's size. */
176 size_t bm_pages = DIV_ROUND_UP (bitmap_buf_size (page_cnt), PGSIZE);
177 if (bm_pages > page_cnt)
178 PANIC ("Not enough memory in %s for bitmap.", name);
179 page_cnt -= bm_pages;
180
181 printf ("%zu pages available in %s.\n", page_cnt, name);
182
183 /* Initialize the pool. */
184 lock_init (&p->lock);
185 p->used_map = bitmap_create_in_buf (page_cnt, base, bm_pages * PGSIZE);
186 p->base = base + bm_pages * PGSIZE;
187}
188
189/* Returns true if PAGE was allocated from POOL,
190 false otherwise. */
191static bool
192page_from_pool (const struct pool *pool, void *page)
193{
194 size_t page_no = pg_no (page);
195 size_t start_page = pg_no (pool->base);
196 size_t end_page = start_page + bitmap_size (pool->used_map);
197
198 return page_no >= start_page && page_no < end_page;
199}
diff --git a/pintos-progos/threads/palloc.h b/pintos-progos/threads/palloc.h
new file mode 100644
index 0000000..d7f4e0b
--- /dev/null
+++ b/pintos-progos/threads/palloc.h
@@ -0,0 +1,20 @@
1#ifndef THREADS_PALLOC_H
2#define THREADS_PALLOC_H
3
4#include <stddef.h>
5
6/* How to allocate pages. */
7enum palloc_flags
8 {
9 PAL_ASSERT = 001, /* Panic on failure. */
10 PAL_ZERO = 002, /* Zero page contents. */
11 PAL_USER = 004 /* User page. */
12 };
13void palloc_init (size_t user_page_limit);
14void *palloc_get_page (enum palloc_flags);
15void *palloc_get_multiple (enum palloc_flags, size_t page_cnt);
16void palloc_free_page (void *);
17void palloc_free_multiple (void *, size_t page_cnt);
18void palloc_dump_used_pages (void);
19
20#endif /* threads/palloc.h */
diff --git a/pintos-progos/threads/pte.h b/pintos-progos/threads/pte.h
new file mode 100644
index 0000000..1660727
--- /dev/null
+++ b/pintos-progos/threads/pte.h
@@ -0,0 +1,107 @@
1#ifndef THREADS_PTE_H
2#define THREADS_PTE_H
3
4#include "threads/vaddr.h"
5
6/* Functions and macros for working with x86 hardware page
7 tables.
8
9 See vaddr.h for more generic functions and macros for virtual
10 addresses.
11
12 Virtual addresses are structured as follows:
13
14 31 22 21 12 11 0
15 +----------------------+----------------------+----------------------+
16 | Page Directory Index | Page Table Index | Page Offset |
17 +----------------------+----------------------+----------------------+
18*/
19
20/* Page table index (bits 12:21). */
21#define PTSHIFT PGBITS /* First page table bit. */
22#define PTBITS 10 /* Number of page table bits. */
23#define PTSPAN (1 << PTBITS << PGBITS) /* Bytes covered by a page table. */
24#define PTMASK BITMASK(PTSHIFT, PTBITS) /* Page table bits (12:21). */
25
26/* Page directory index (bits 22:31). */
27#define PDSHIFT (PTSHIFT + PTBITS) /* First page directory bit. */
28#define PDBITS 10 /* Number of page dir bits. */
29#define PDMASK BITMASK(PDSHIFT, PDBITS) /* Page directory bits (22:31). */
30
31/* Obtains page table index from a virtual address. */
32static inline unsigned pt_no (const void *va) {
33 return ((uintptr_t) va & PTMASK) >> PTSHIFT;
34}
35
36/* Obtains page directory index from a virtual address. */
37static inline uintptr_t pd_no (const void *va) {
38 return (uintptr_t) va >> PDSHIFT;
39}
40
41/* Page directory and page table entries.
42
43 For more information see the section on page tables in the
44 Pintos reference guide chapter, or [IA32-v3a] 3.7.6
45 "Page-Directory and Page-Table Entries".
46
47 PDEs and PTEs share a common format:
48
49 31 12 11 0
50 +------------------------------------+------------------------+
51 | Physical Address | Flags |
52 +------------------------------------+------------------------+
53
54 In a PDE, the physical address points to a page table.
55 In a PTE, the physical address points to a data or code page.
56 The important flags are listed below.
57 When a PDE or PTE is not "present", the other flags are
58 ignored.
59 A PDE or PTE that is initialized to 0 will be interpreted as
60 "not present", which is just fine. */
61#define PTE_FLAGS 0x00000fff /* Flag bits. */
62#define PTE_ADDR 0xfffff000 /* Address bits. */
63#define PTE_AVL 0x00000e00 /* Bits available for OS use. */
64#define PTE_P 0x1 /* 1=present, 0=not present. */
65#define PTE_W 0x2 /* 1=read/write, 0=read-only. */
66#define PTE_U 0x4 /* 1=user/kernel, 0=kernel only. */
67#define PTE_A 0x20 /* 1=accessed, 0=not acccessed. */
68#define PTE_D 0x40 /* 1=dirty, 0=not dirty (PTEs only). */
69
70/* Returns a PDE that points to page table PT. */
71static inline uint32_t pde_create (uint32_t *pt) {
72 ASSERT (pg_ofs (pt) == 0);
73 return vtop (pt) | PTE_U | PTE_P | PTE_W;
74}
75
76/* Returns a pointer to the page table that page directory entry
77 PDE, which must "present", points to. */
78static inline uint32_t *pde_get_pt (uint32_t pde) {
79 ASSERT (pde & PTE_P);
80 return ptov (pde & PTE_ADDR);
81}
82
83/* Returns a PTE that points to PAGE.
84 The PTE's page is readable.
85 If WRITABLE is true then it will be writable as well.
86 The page will be usable only by ring 0 code (the kernel). */
87static inline uint32_t pte_create_kernel (void *page, bool writable) {
88 ASSERT (pg_ofs (page) == 0);
89 return vtop (page) | PTE_P | (writable ? PTE_W : 0);
90}
91
92/* Returns a PTE that points to PAGE.
93 The PTE's page is readable.
94 If WRITABLE is true then it will be writable as well.
95 The page will be usable by both user and kernel code. */
96static inline uint32_t pte_create_user (void *page, bool writable) {
97 return pte_create_kernel (page, writable) | PTE_U;
98}
99
100/* Returns a pointer to the page that page table entry PTE points
101 to. */
102static inline void *pte_get_page (uint32_t pte) {
103 return ptov (pte & PTE_ADDR);
104}
105
106#endif /* threads/pte.h */
107
diff --git a/pintos-progos/threads/start.S b/pintos-progos/threads/start.S
new file mode 100644
index 0000000..29ffa7a
--- /dev/null
+++ b/pintos-progos/threads/start.S
@@ -0,0 +1,204 @@
1 #include "threads/loader.h"
2
3#### Kernel startup code.
4
5#### The loader (in loader.S) loads the kernel at physical address
6#### 0x20000 (128 kB) and jumps to "start", defined here. This code
7#### switches from real mode to 32-bit protected mode and calls
8#### main().
9
10/* Flags in control register 0. */
11#define CR0_PE 0x00000001 /* Protection Enable. */
12#define CR0_EM 0x00000004 /* (Floating-point) Emulation. */
13#define CR0_PG 0x80000000 /* Paging. */
14#define CR0_WP 0x00010000 /* Write-Protect enable in kernel mode. */
15
16 .section .start
17
18# The following code runs in real mode, which is a 16-bit code segment.
19 .code16
20
21.func start
22.globl start
23start:
24
25# The loader called into us with CS = 0x2000, SS = 0x0000, ESP = 0xf000,
26# but we should initialize the other segment registers.
27
28 mov $0x2000, %ax
29 mov %ax, %ds
30 mov %ax, %es
31
32# Set string instructions to go upward.
33 cld
34
35#### Get memory size, via interrupt 15h function 88h (see [IntrList]),
36#### which returns AX = (kB of physical memory) - 1024. This only
37#### works for memory sizes <= 65 MB, which should be fine for our
38#### purposes. We cap memory at 64 MB because that's all we prepare
39#### page tables for, below.
40
41 movb $0x88, %ah
42 int $0x15
43 addl $1024, %eax # Total kB memory
44 cmp $0x10000, %eax # Cap at 64 MB
45 jbe 1f
46 mov $0x10000, %eax
471: shrl $2, %eax # Total 4 kB pages
48 addr32 movl %eax, init_ram_pages - LOADER_PHYS_BASE - 0x20000
49
50#### Enable A20. Address line 20 is tied low when the machine boots,
51#### which prevents addressing memory about 1 MB. This code fixes it.
52
53# Poll status register while busy.
54
551: inb $0x64, %al
56 testb $0x2, %al
57 jnz 1b
58
59# Send command for writing output port.
60
61 movb $0xd1, %al
62 outb %al, $0x64
63
64# Poll status register while busy.
65
661: inb $0x64, %al
67 testb $0x2, %al
68 jnz 1b
69
70# Enable A20 line.
71
72 movb $0xdf, %al
73 outb %al, $0x60
74
75# Poll status register while busy.
76
771: inb $0x64, %al
78 testb $0x2, %al
79 jnz 1b
80
81#### Create temporary page directory and page table and set page
82#### directory base register.
83
84# Create page directory at 0xf000 (60 kB) and fill with zeroes.
85 mov $0xf00, %ax
86 mov %ax, %es
87 subl %eax, %eax
88 subl %edi, %edi
89 movl $0x400, %ecx
90 rep stosl
91
92# Add PDEs to point to page tables for the first 64 MB of RAM.
93# Also add identical PDEs starting at LOADER_PHYS_BASE.
94# See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries"
95# for a description of the bits in %eax.
96
97 movl $0x10007, %eax
98 movl $0x11, %ecx
99 subl %edi, %edi
1001: movl %eax, %es:(%di)
101 movl %eax, %es:LOADER_PHYS_BASE >> 20(%di)
102 addw $4, %di
103 addl $0x1000, %eax
104 loop 1b
105
106# Set up page tables for one-to-map linear to physical map for the
107# first 64 MB of RAM.
108# See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries"
109# for a description of the bits in %eax.
110
111 movw $0x1000, %ax
112 movw %ax, %es
113 movl $0x7, %eax
114 movl $0x4000, %ecx
115 subl %edi, %edi
1161: movl %eax, %es:(%di)
117 addw $4, %di
118 addl $0x1000, %eax
119 loop 1b
120
121# Set page directory base register.
122
123 movl $0xf000, %eax
124 movl %eax, %cr3
125
126#### Switch to protected mode.
127
128# First, disable interrupts. We won't set up the IDT until we get
129# into C code, so any interrupt would blow us away.
130
131 cli
132
133# Protected mode requires a GDT, so point the GDTR to our GDT.
134# We need a data32 prefix to ensure that all 32 bits of the GDT
135# descriptor are loaded (default is to load only 24 bits).
136# The CPU doesn't need an addr32 prefix but ELF doesn't do 16-bit
137# relocations.
138
139 data32 addr32 lgdt gdtdesc - LOADER_PHYS_BASE - 0x20000
140
141# Then we turn on the following bits in CR0:
142# PE (Protect Enable): this turns on protected mode.
143# PG (Paging): turns on paging.
144# WP (Write Protect): if unset, ring 0 code ignores
145# write-protect bits in page tables (!).
146# EM (Emulation): forces floating-point instructions to trap.
147# We don't support floating point.
148
149 movl %cr0, %eax
150 orl $CR0_PE | CR0_PG | CR0_WP | CR0_EM, %eax
151 movl %eax, %cr0
152
153# We're now in protected mode in a 16-bit segment. The CPU still has
154# the real-mode code segment cached in %cs's segment descriptor. We
155# need to reload %cs, and the easiest way is to use a far jump.
156# Because we're not running in a 32-bit segment the data32 prefix is
157# needed to jump to a 32-bit offset in the target segment.
158
159 data32 ljmp $SEL_KCSEG, $1f
160
161# We're now in protected mode in a 32-bit segment.
162# Let the assembler know.
163
164 .code32
165
166# Reload all the other segment registers and the stack pointer to
167# point into our new GDT.
168
1691: mov $SEL_KDSEG, %ax
170 mov %ax, %ds
171 mov %ax, %es
172 mov %ax, %fs
173 mov %ax, %gs
174 mov %ax, %ss
175 addl $LOADER_PHYS_BASE, %esp
176 movl $0, %ebp # Null-terminate main()'s backtrace
177
178#### Call main().
179
180 call main
181
182# main() shouldn't ever return. If it does, spin.
183
1841: jmp 1b
185.endfunc
186
187#### GDT
188
189 .align 8
190gdt:
191 .quad 0x0000000000000000 # Null segment. Not used by CPU.
192 .quad 0x00cf9a000000ffff # System code, base 0, limit 4 GB.
193 .quad 0x00cf92000000ffff # System data, base 0, limit 4 GB.
194
195gdtdesc:
196 .word gdtdesc - gdt - 1 # Size of the GDT, minus 1 byte.
197 .long gdt # Address of the GDT.
198
199#### Physical memory size in 4 kB pages. This is exported to the rest
200#### of the kernel.
201.globl init_ram_pages
202init_ram_pages:
203 .long 0
204
diff --git a/pintos-progos/threads/switch.S b/pintos-progos/threads/switch.S
new file mode 100644
index 0000000..feca86c
--- /dev/null
+++ b/pintos-progos/threads/switch.S
@@ -0,0 +1,65 @@
1#include "threads/switch.h"
2
3#### struct thread *switch_threads (struct thread *cur, struct thread *next);
4####
5#### Switches from CUR, which must be the running thread, to NEXT,
6#### which must also be running switch_threads(), returning CUR in
7#### NEXT's context.
8####
9#### This function works by assuming that the thread we're switching
10#### into is also running switch_threads(). Thus, all it has to do is
11#### preserve a few registers on the stack, then switch stacks and
12#### restore the registers. As part of switching stacks we record the
13#### current stack pointer in CUR's thread structure.
14
15.globl switch_threads
16.func switch_threads
17switch_threads:
18 # Save caller's register state.
19 #
20 # Note that the SVR4 ABI allows us to destroy %eax, %ecx, %edx,
21 # but requires us to preserve %ebx, %ebp, %esi, %edi. See
22 # [SysV-ABI-386] pages 3-11 and 3-12 for details.
23 #
24 # This stack frame must match the one set up by thread_create()
25 # in size.
26 pushl %ebx
27 pushl %ebp
28 pushl %esi
29 pushl %edi
30
31 # Get offsetof (struct thread, stack).
32.globl thread_stack_ofs
33 mov thread_stack_ofs, %edx
34
35 # Save current stack pointer to old thread's stack, if any.
36 movl SWITCH_CUR(%esp), %eax
37 movl %esp, (%eax,%edx,1)
38
39 # Restore stack pointer from new thread's stack.
40 movl SWITCH_NEXT(%esp), %ecx
41 movl (%ecx,%edx,1), %esp
42
43 # Restore caller's register state.
44 popl %edi
45 popl %esi
46 popl %ebp
47 popl %ebx
48 ret
49.endfunc
50
51.globl switch_entry
52.func switch_entry
53switch_entry:
54 # Discard switch_threads() arguments.
55 addl $8, %esp
56
57 # Call thread_schedule_tail(prev).
58 pushl %eax
59.globl thread_schedule_tail
60 call thread_schedule_tail
61 addl $4, %esp
62
63 # Start thread proper.
64 ret
65.endfunc
diff --git a/pintos-progos/threads/switch.h b/pintos-progos/threads/switch.h
new file mode 100644
index 0000000..cc156b6
--- /dev/null
+++ b/pintos-progos/threads/switch.h
@@ -0,0 +1,39 @@
1#ifndef THREADS_SWITCH_H
2#define THREADS_SWITCH_H
3
4#ifndef __ASSEMBLER__
5/* switch_thread()'s stack frame. */
6struct switch_threads_frame
7 {
8 uint32_t edi; /* 0: Saved %edi. */
9 uint32_t esi; /* 4: Saved %esi. */
10 uint32_t ebp; /* 8: Saved %ebp. */
11 uint32_t ebx; /* 12: Saved %ebx. */
12 void (*eip) (void); /* 16: Return address. */
13 struct thread *cur; /* 20: switch_threads()'s CUR argument. */
14 struct thread *next; /* 24: switch_threads()'s NEXT argument. */
15 };
16
17/* Switches from CUR, which must be the running thread, to NEXT,
18 which must also be running switch_threads(), returning CUR in
19 NEXT's context. */
20struct thread *switch_threads (struct thread *cur, struct thread *next);
21
22/* Stack frame for switch_entry(). */
23struct switch_entry_frame
24 {
25 void (*eip) (void);
26 };
27
28void switch_entry (void);
29
30/* Pops the CUR and NEXT arguments off the stack, for use in
31 initializing threads. */
32void switch_thunk (void);
33#endif
34
35/* Offsets used by switch.S. */
36#define SWITCH_CUR 20
37#define SWITCH_NEXT 24
38
39#endif /* threads/switch.h */
diff --git a/pintos-progos/threads/synch.c b/pintos-progos/threads/synch.c
new file mode 100644
index 0000000..317c68a
--- /dev/null
+++ b/pintos-progos/threads/synch.c
@@ -0,0 +1,338 @@
1/* This file is derived from source code for the Nachos
2 instructional operating system. The Nachos copyright notice
3 is reproduced in full below. */
4
5/* Copyright (c) 1992-1996 The Regents of the University of California.
6 All rights reserved.
7
8 Permission to use, copy, modify, and distribute this software
9 and its documentation for any purpose, without fee, and
10 without written agreement is hereby granted, provided that the
11 above copyright notice and the following two paragraphs appear
12 in all copies of this software.
13
14 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO
15 ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
16 CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE
17 AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA
18 HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
19
20 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
21 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23 PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
24 BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
25 PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
26 MODIFICATIONS.
27*/
28
29#include "threads/synch.h"
30#include <stdio.h>
31#include <string.h>
32#include "threads/interrupt.h"
33#include "threads/thread.h"
34
35/* Initializes semaphore SEMA to VALUE. A semaphore is a
36 nonnegative integer along with two atomic operators for
37 manipulating it:
38
39 - down or "P": wait for the value to become positive, then
40 decrement it.
41
42 - up or "V": increment the value (and wake up one waiting
43 thread, if any). */
44void
45sema_init (struct semaphore *sema, unsigned value)
46{
47 ASSERT (sema != NULL);
48
49 sema->value = value;
50 list_init (&sema->waiters);
51}
52
53/* Down or "P" operation on a semaphore. Waits for SEMA's value
54 to become positive and then atomically decrements it.
55
56 This function may sleep, so it must not be called within an
57 interrupt handler. This function may be called with
58 interrupts disabled, but if it sleeps then the next scheduled
59 thread will probably turn interrupts back on. */
60void
61sema_down (struct semaphore *sema)
62{
63 enum intr_level old_level;
64
65 ASSERT (sema != NULL);
66 ASSERT (!intr_context ());
67
68 old_level = intr_disable ();
69 while (sema->value == 0)
70 {
71 list_push_back (&sema->waiters, &thread_current ()->elem);
72 thread_block ();
73 }
74 sema->value--;
75 intr_set_level (old_level);
76}
77
78/* Down or "P" operation on a semaphore, but only if the
79 semaphore is not already 0. Returns true if the semaphore is
80 decremented, false otherwise.
81
82 This function may be called from an interrupt handler. */
83bool
84sema_try_down (struct semaphore *sema)
85{
86 enum intr_level old_level;
87 bool success;
88
89 ASSERT (sema != NULL);
90
91 old_level = intr_disable ();
92 if (sema->value > 0)
93 {
94 sema->value--;
95 success = true;
96 }
97 else
98 success = false;
99 intr_set_level (old_level);
100
101 return success;
102}
103
104/* Up or "V" operation on a semaphore. Increments SEMA's value
105 and wakes up one thread of those waiting for SEMA, if any.
106
107 This function may be called from an interrupt handler. */
108void
109sema_up (struct semaphore *sema)
110{
111 enum intr_level old_level;
112
113 ASSERT (sema != NULL);
114
115 old_level = intr_disable ();
116 if (!list_empty (&sema->waiters))
117 thread_unblock (list_entry (list_pop_front (&sema->waiters),
118 struct thread, elem));
119 sema->value++;
120 intr_set_level (old_level);
121}
122
123static void sema_test_helper (void *sema_);
124
125/* Self-test for semaphores that makes control "ping-pong"
126 between a pair of threads. Insert calls to printf() to see
127 what's going on. */
128void
129sema_self_test (void)
130{
131 struct semaphore sema[2];
132 int i;
133
134 printf ("Testing semaphores...");
135 sema_init (&sema[0], 0);
136 sema_init (&sema[1], 0);
137 thread_create ("sema-test", PRI_DEFAULT, sema_test_helper, &sema);
138 for (i = 0; i < 10; i++)
139 {
140 sema_up (&sema[0]);
141 sema_down (&sema[1]);
142 }
143 printf ("done.\n");
144}
145
146/* Thread function used by sema_self_test(). */
147static void
148sema_test_helper (void *sema_)
149{
150 struct semaphore *sema = sema_;
151 int i;
152
153 for (i = 0; i < 10; i++)
154 {
155 sema_down (&sema[0]);
156 sema_up (&sema[1]);
157 }
158}
159
160/* Initializes LOCK. A lock can be held by at most a single
161 thread at any given time. Our locks are not "recursive", that
162 is, it is an error for the thread currently holding a lock to
163 try to acquire that lock.
164
165 A lock is a specialization of a semaphore with an initial
166 value of 1. The difference between a lock and such a
167 semaphore is twofold. First, a semaphore can have a value
168 greater than 1, but a lock can only be owned by a single
169 thread at a time. Second, a semaphore does not have an owner,
170 meaning that one thread can "down" the semaphore and then
171 another one "up" it, but with a lock the same thread must both
172 acquire and release it. When these restrictions prove
173 onerous, it's a good sign that a semaphore should be used,
174 instead of a lock. */
175void
176lock_init (struct lock *lock)
177{
178 ASSERT (lock != NULL);
179
180 lock->holder = NULL;
181 sema_init (&lock->semaphore, 1);
182}
183
184/* Acquires LOCK, sleeping until it becomes available if
185 necessary. The lock must not already be held by the current
186 thread.
187
188 This function may sleep, so it must not be called within an
189 interrupt handler. This function may be called with
190 interrupts disabled, but interrupts will be turned back on if
191 we need to sleep. */
192void
193lock_acquire (struct lock *lock)
194{
195 ASSERT (lock != NULL);
196 ASSERT (!intr_context ());
197 ASSERT (!lock_held_by_current_thread (lock));
198
199 sema_down (&lock->semaphore);
200 lock->holder = thread_current ();
201}
202
203/* Tries to acquires LOCK and returns true if successful or false
204 on failure. The lock must not already be held by the current
205 thread.
206
207 This function will not sleep, so it may be called within an
208 interrupt handler. */
209bool
210lock_try_acquire (struct lock *lock)
211{
212 bool success;
213
214 ASSERT (lock != NULL);
215 ASSERT (!lock_held_by_current_thread (lock));
216
217 success = sema_try_down (&lock->semaphore);
218 if (success)
219 lock->holder = thread_current ();
220 return success;
221}
222
223/* Releases LOCK, which must be owned by the current thread.
224
225 An interrupt handler cannot acquire a lock, so it does not
226 make sense to try to release a lock within an interrupt
227 handler. */
228void
229lock_release (struct lock *lock)
230{
231 ASSERT (lock != NULL);
232 ASSERT (lock_held_by_current_thread (lock));
233
234 lock->holder = NULL;
235 sema_up (&lock->semaphore);
236}
237
238/* Returns true if the current thread holds LOCK, false
239 otherwise. (Note that testing whether some other thread holds
240 a lock would be racy.) */
241bool
242lock_held_by_current_thread (const struct lock *lock)
243{
244 ASSERT (lock != NULL);
245
246 return lock->holder == thread_current ();
247}
248
249/* One semaphore in a list. */
250struct semaphore_elem
251 {
252 struct list_elem elem; /* List element. */
253 struct semaphore semaphore; /* This semaphore. */
254 };
255
256/* Initializes condition variable COND. A condition variable
257 allows one piece of code to signal a condition and cooperating
258 code to receive the signal and act upon it. */
259void
260cond_init (struct condition *cond)
261{
262 ASSERT (cond != NULL);
263
264 list_init (&cond->waiters);
265}
266
267/* Atomically releases LOCK and waits for COND to be signaled by
268 some other piece of code. After COND is signaled, LOCK is
269 reacquired before returning. LOCK must be held before calling
270 this function.
271
272 The monitor implemented by this function is "Mesa" style, not
273 "Hoare" style, that is, sending and receiving a signal are not
274 an atomic operation. Thus, typically the caller must recheck
275 the condition after the wait completes and, if necessary, wait
276 again.
277
278 A given condition variable is associated with only a single
279 lock, but one lock may be associated with any number of
280 condition variables. That is, there is a one-to-many mapping
281 from locks to condition variables.
282
283 This function may sleep, so it must not be called within an
284 interrupt handler. This function may be called with
285 interrupts disabled, but interrupts will be turned back on if
286 we need to sleep. */
287void
288cond_wait (struct condition *cond, struct lock *lock)
289{
290 struct semaphore_elem waiter;
291
292 ASSERT (cond != NULL);
293 ASSERT (lock != NULL);
294 ASSERT (!intr_context ());
295 ASSERT (lock_held_by_current_thread (lock));
296
297 sema_init (&waiter.semaphore, 0);
298 list_push_back (&cond->waiters, &waiter.elem);
299 lock_release (lock);
300 sema_down (&waiter.semaphore);
301 lock_acquire (lock);
302}
303
304/* If any threads are waiting on COND (protected by LOCK), then
305 this function signals one of them to wake up from its wait.
306 LOCK must be held before calling this function.
307
308 An interrupt handler cannot acquire a lock, so it does not
309 make sense to try to signal a condition variable within an
310 interrupt handler. */
311void
312cond_signal (struct condition *cond, struct lock *lock UNUSED)
313{
314 ASSERT (cond != NULL);
315 ASSERT (lock != NULL);
316 ASSERT (!intr_context ());
317 ASSERT (lock_held_by_current_thread (lock));
318
319 if (!list_empty (&cond->waiters))
320 sema_up (&list_entry (list_pop_front (&cond->waiters),
321 struct semaphore_elem, elem)->semaphore);
322}
323
324/* Wakes up all threads, if any, waiting on COND (protected by
325 LOCK). LOCK must be held before calling this function.
326
327 An interrupt handler cannot acquire a lock, so it does not
328 make sense to try to signal a condition variable within an
329 interrupt handler. */
330void
331cond_broadcast (struct condition *cond, struct lock *lock)
332{
333 ASSERT (cond != NULL);
334 ASSERT (lock != NULL);
335
336 while (!list_empty (&cond->waiters))
337 cond_signal (cond, lock);
338}
diff --git a/pintos-progos/threads/synch.h b/pintos-progos/threads/synch.h
new file mode 100644
index 0000000..a19e88b
--- /dev/null
+++ b/pintos-progos/threads/synch.h
@@ -0,0 +1,51 @@
1#ifndef THREADS_SYNCH_H
2#define THREADS_SYNCH_H
3
4#include <list.h>
5#include <stdbool.h>
6
7/* A counting semaphore. */
8struct semaphore
9 {
10 unsigned value; /* Current value. */
11 struct list waiters; /* List of waiting threads. */
12 };
13
14void sema_init (struct semaphore *, unsigned value);
15void sema_down (struct semaphore *);
16bool sema_try_down (struct semaphore *);
17void sema_up (struct semaphore *);
18void sema_self_test (void);
19
20/* Lock. */
21struct lock
22 {
23 struct thread *holder; /* Thread holding lock (for debugging). */
24 struct semaphore semaphore; /* Binary semaphore controlling access. */
25 };
26
27void lock_init (struct lock *);
28void lock_acquire (struct lock *);
29bool lock_try_acquire (struct lock *);
30void lock_release (struct lock *);
31bool lock_held_by_current_thread (const struct lock *);
32
33/* Condition variable. */
34struct condition
35 {
36 struct list waiters; /* List of waiting threads. */
37 };
38
39void cond_init (struct condition *);
40void cond_wait (struct condition *, struct lock *);
41void cond_signal (struct condition *, struct lock *);
42void cond_broadcast (struct condition *, struct lock *);
43
44/* Optimization barrier.
45
46 The compiler will not reorder operations across an
47 optimization barrier. See "Optimization Barriers" in the
48 reference guide for more information.*/
49#define barrier() asm volatile ("" : : : "memory")
50
51#endif /* threads/synch.h */
diff --git a/pintos-progos/threads/thread.c b/pintos-progos/threads/thread.c
new file mode 100644
index 0000000..845f059
--- /dev/null
+++ b/pintos-progos/threads/thread.c
@@ -0,0 +1,594 @@
1#include "threads/thread.h"
2#include <debug.h>
3#include <stddef.h>
4#include <random.h>
5#include <stdio.h>
6#include <string.h>
7#include "threads/flags.h"
8#include "threads/interrupt.h"
9#include "threads/intr-stubs.h"
10#include "threads/palloc.h"
11#include "threads/switch.h"
12#include "threads/synch.h"
13#include "threads/vaddr.h"
14#ifdef USERPROG
15#include "userprog/process.h"
16#endif
17
18/* Random value for struct thread's `magic' member.
19 Used to detect stack overflow. See the big comment at the top
20 of thread.h for details. */
21#define THREAD_MAGIC 0xcd6abf4b
22
23/* List of processes in THREAD_READY state, that is, processes
24 that are ready to run but not actually running. */
25static struct list ready_list;
26
27/* List of all processes. Processes are added to this list
28 when they are first scheduled and removed when they exit. */
29static struct list all_list;
30
31/* Idle thread. */
32static struct thread *idle_thread;
33
34/* Initial thread, the thread running init.c:main(). */
35static struct thread *initial_thread;
36
37/* Lock used by allocate_tid(). */
38static struct lock tid_lock;
39
40/* Stack frame for kernel_thread(). */
41struct kernel_thread_frame
42 {
43 void *eip; /* Return address. */
44 thread_func *function; /* Function to call. */
45 void *aux; /* Auxiliary data for function. */
46 };
47
48/* Statistics. */
49static long long idle_ticks; /* # of timer ticks spent idle. */
50static long long kernel_ticks; /* # of timer ticks in kernel threads. */
51static long long user_ticks; /* # of timer ticks in user programs. */
52
53/* Scheduling. */
54#define TIME_SLICE 4 /* # of timer ticks to give each thread. */
55static unsigned thread_ticks; /* # of timer ticks since last yield. */
56
57/* If false (default), use round-robin scheduler.
58 If true, use multi-level feedback queue scheduler.
59 Controlled by kernel command-line option "-o mlfqs". */
60bool thread_mlfqs;
61
62static void kernel_thread (thread_func *, void *aux);
63
64static void idle (void *aux UNUSED);
65static struct thread *running_thread (void);
66static struct thread *next_thread_to_run (void);
67static void init_thread (struct thread *, const char *name, int priority);
68static bool is_thread (struct thread *) UNUSED;
69static void *alloc_frame (struct thread *, size_t size);
70static void schedule (void);
71void thread_schedule_tail (struct thread *prev);
72static tid_t allocate_tid (void);
73
74/* Initializes the threading system by transforming the code
75 that's currently running into a thread. This can't work in
76 general and it is possible in this case only because loader.S
77 was careful to put the bottom of the stack at a page boundary.
78
79 Also initializes the run queue and the tid lock.
80
81 After calling this function, be sure to initialize the page
82 allocator before trying to create any threads with
83 thread_create().
84
85 It is not safe to call thread_current() until this function
86 finishes. */
87void
88thread_init (void)
89{
90 ASSERT (intr_get_level () == INTR_OFF);
91
92 lock_init (&tid_lock);
93 list_init (&ready_list);
94 list_init (&all_list);
95
96#ifdef USERPROG
97 process_init ();
98#endif
99
100 /* Set up a thread structure for the running thread. */
101 initial_thread = running_thread ();
102 init_thread (initial_thread, "main", PRI_DEFAULT);
103 initial_thread->status = THREAD_RUNNING;
104 initial_thread->tid = allocate_tid ();
105}
106
107/* Starts preemptive thread scheduling by enabling interrupts.
108 Also creates the idle thread. */
109void
110thread_start (void)
111{
112 /* Create the idle thread. */
113 struct semaphore idle_started;
114 sema_init (&idle_started, 0);
115 thread_create ("idle", PRI_MIN, idle, &idle_started);
116
117 /* Start preemptive thread scheduling. */
118 intr_enable ();
119
120 /* Wait for the idle thread to initialize idle_thread. */
121 sema_down (&idle_started);
122}
123
124/* Called by the timer interrupt handler at each timer tick.
125 Thus, this function runs in an external interrupt context. */
126void
127thread_tick (void)
128{
129 struct thread *t = thread_current ();
130
131 /* Update statistics. */
132 if (t == idle_thread)
133 idle_ticks++;
134#ifdef USERPROG
135 else if (t->pagedir != NULL)
136 user_ticks++;
137#endif
138 else
139 kernel_ticks++;
140
141 /* Enforce preemption. */
142 if (++thread_ticks >= TIME_SLICE)
143 intr_yield_on_return ();
144}
145
146/* Prints thread statistics. */
147void
148thread_print_stats (void)
149{
150 printf ("Thread: %lld idle ticks, %lld kernel ticks, %lld user ticks\n",
151 idle_ticks, kernel_ticks, user_ticks);
152}
153
154/* Creates a new kernel thread named NAME with the given initial
155 PRIORITY, which executes FUNCTION passing AUX as the argument,
156 and adds it to the ready queue. Returns the thread identifier
157 for the new thread, or TID_ERROR if creation fails.
158
159 If thread_start() has been called, then the new thread may be
160 scheduled before thread_create() returns. It could even exit
161 before thread_create() returns. Contrariwise, the original
162 thread may run for any amount of time before the new thread is
163 scheduled. Use a semaphore or some other form of
164 synchronization if you need to ensure ordering.
165
166 The code provided sets the new thread's `priority' member to
167 PRIORITY, but no actual priority scheduling is implemented.
168 Priority scheduling is the goal of Problem 1-3. */
169tid_t
170thread_create (const char *name, int priority,
171 thread_func *function, void *aux)
172{
173 struct thread *t;
174 struct kernel_thread_frame *kf;
175 struct switch_entry_frame *ef;
176 struct switch_threads_frame *sf;
177 tid_t tid;
178 enum intr_level old_level;
179
180 ASSERT (function != NULL);
181
182 /* Allocate thread. */
183 t = palloc_get_page (PAL_ZERO);
184 if (t == NULL)
185 return TID_ERROR;
186
187 /* Initialize thread. */
188 init_thread (t, name, priority);
189 tid = t->tid = allocate_tid ();
190
191 /* Prepare thread for first run by initializing its stack.
192 Do this atomically so intermediate values for the 'stack'
193 member cannot be observed. */
194 old_level = intr_disable ();
195
196 /* Stack frame for kernel_thread(). */
197 kf = alloc_frame (t, sizeof *kf);
198 kf->eip = NULL;
199 kf->function = function;
200 kf->aux = aux;
201
202 /* Stack frame for switch_entry(). */
203 ef = alloc_frame (t, sizeof *ef);
204 ef->eip = (void (*) (void)) kernel_thread;
205
206 /* Stack frame for switch_threads(). */
207 sf = alloc_frame (t, sizeof *sf);
208 sf->eip = switch_entry;
209 sf->ebp = 0;
210
211 intr_set_level (old_level);
212
213 /* Add to run queue. */
214 thread_unblock (t);
215
216 return tid;
217}
218
219/* Puts the current thread to sleep. It will not be scheduled
220 again until awoken by thread_unblock().
221
222 This function must be called with interrupts turned off. It
223 is usually a better idea to use one of the synchronization
224 primitives in synch.h. */
225void
226thread_block (void)
227{
228 ASSERT (!intr_context ());
229 ASSERT (intr_get_level () == INTR_OFF);
230
231 thread_current ()->status = THREAD_BLOCKED;
232 schedule ();
233}
234
235/* Transitions a blocked thread T to the ready-to-run state.
236 This is an error if T is not blocked. (Use thread_yield() to
237 make the running thread ready.)
238
239 This function does not preempt the running thread. This can
240 be important: if the caller had disabled interrupts itself,
241 it may expect that it can atomically unblock a thread and
242 update other data. */
243void
244thread_unblock (struct thread *t)
245{
246 enum intr_level old_level;
247
248 ASSERT (is_thread (t));
249
250 old_level = intr_disable ();
251 ASSERT (t->status == THREAD_BLOCKED);
252 list_push_back (&ready_list, &t->elem);
253 t->status = THREAD_READY;
254 intr_set_level (old_level);
255}
256
257/* Returns the name of the running thread. */
258const char *
259thread_name (void)
260{
261 return thread_current ()->name;
262}
263
264/* Returns the running thread.
265 This is running_thread() plus a couple of sanity checks.
266 See the big comment at the top of thread.h for details. */
267struct thread *
268thread_current (void)
269{
270 struct thread *t = running_thread ();
271
272 /* Make sure T is really a thread.
273 If either of these assertions fire, then your thread may
274 have overflowed its stack. Each thread has less than 4 kB
275 of stack, so a few big automatic arrays or moderate
276 recursion can cause stack overflow. */
277 ASSERT (is_thread (t));
278 ASSERT (t->status == THREAD_RUNNING);
279
280 return t;
281}
282
283/* Returns the running thread's tid. */
284tid_t
285thread_tid (void)
286{
287 return thread_current ()->tid;
288}
289
290/* Deschedules the current thread and destroys it. Never
291 returns to the caller. */
292void
293thread_exit (void)
294{
295 ASSERT (!intr_context ());
296
297#ifdef USERPROG
298 process_exit ();
299#endif
300
301 /* Remove thread from all threads list, set our status to dying,
302 and schedule another process. That process will destroy us
303 when it calls thread_schedule_tail(). */
304 intr_disable ();
305 list_remove (&thread_current()->allelem);
306 thread_current ()->status = THREAD_DYING;
307 schedule ();
308 NOT_REACHED ();
309}
310
311/* Yields the CPU. The current thread is not put to sleep and
312 may be scheduled again immediately at the scheduler's whim. */
313void
314thread_yield (void)
315{
316 struct thread *cur = thread_current ();
317 enum intr_level old_level;
318
319 ASSERT (!intr_context ());
320
321 old_level = intr_disable ();
322 if (cur != idle_thread)
323 list_push_back (&ready_list, &cur->elem);
324 cur->status = THREAD_READY;
325 schedule ();
326 intr_set_level (old_level);
327}
328
329/* Invoke function 'func' on all threads, passing along 'aux'.
330 This function must be called with interrupts off. */
331void
332thread_foreach (thread_action_func *func, void *aux)
333{
334 struct list_elem *e;
335
336 ASSERT (intr_get_level () == INTR_OFF);
337
338 for (e = list_begin (&all_list); e != list_end (&all_list);
339 e = list_next (e))
340 {
341 struct thread *t = list_entry (e, struct thread, allelem);
342 func (t, aux);
343 }
344}
345
346/* Sets the current thread's priority to NEW_PRIORITY. */
347void
348thread_set_priority (int new_priority)
349{
350 thread_current ()->priority = new_priority;
351}
352
353/* Returns the current thread's priority. */
354int
355thread_get_priority (void)
356{
357 return thread_current ()->priority;
358}
359
360/* Sets the current thread's nice value to NICE. */
361void
362thread_set_nice (int nice UNUSED)
363{
364 /* Not yet implemented. */
365}
366
367/* Returns the current thread's nice value. */
368int
369thread_get_nice (void)
370{
371 /* Not yet implemented. */
372 return 0;
373}
374
375/* Returns 100 times the system load average. */
376int
377thread_get_load_avg (void)
378{
379 /* Not yet implemented. */
380 return 0;
381}
382
383/* Returns 100 times the current thread's recent_cpu value. */
384int
385thread_get_recent_cpu (void)
386{
387 /* Not yet implemented. */
388 return 0;
389}
390
391/* Idle thread. Executes when no other thread is ready to run.
392
393 The idle thread is initially put on the ready list by
394 thread_start(). It will be scheduled once initially, at which
395 point it initializes idle_thread, "up"s the semaphore passed
396 to it to enable thread_start() to continue, and immediately
397 blocks. After that, the idle thread never appears in the
398 ready list. It is returned by next_thread_to_run() as a
399 special case when the ready list is empty. */
400static void
401idle (void *idle_started_ UNUSED)
402{
403 struct semaphore *idle_started = idle_started_;
404 idle_thread = thread_current ();
405 sema_up (idle_started);
406
407 for (;;)
408 {
409 /* Let someone else run. */
410 intr_disable ();
411 thread_block ();
412
413 /* Re-enable interrupts and wait for the next one.
414
415 The `sti' instruction disables interrupts until the
416 completion of the next instruction, so these two
417 instructions are executed atomically. This atomicity is
418 important; otherwise, an interrupt could be handled
419 between re-enabling interrupts and waiting for the next
420 one to occur, wasting as much as one clock tick worth of
421 time.
422
423 See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3a]
424 7.11.1 "HLT Instruction". */
425 asm volatile ("sti; hlt" : : : "memory");
426 }
427}
428
429/* Function used as the basis for a kernel thread. */
430static void
431kernel_thread (thread_func *function, void *aux)
432{
433 ASSERT (function != NULL);
434
435 intr_enable (); /* The scheduler runs with interrupts off. */
436 function (aux); /* Execute the thread function. */
437 thread_exit (); /* If function() returns, kill the thread. */
438}
439
440/* Returns the running thread. */
441struct thread *
442running_thread (void)
443{
444 uint32_t *esp;
445
446 /* Copy the CPU's stack pointer into `esp', and then round that
447 down to the start of a page. Because `struct thread' is
448 always at the beginning of a page and the stack pointer is
449 somewhere in the middle, this locates the curent thread. */
450 asm ("mov %%esp, %0" : "=g" (esp));
451 return pg_round_down (esp);
452}
453
454/* Returns true if T appears to point to a valid thread. */
455static bool
456is_thread (struct thread *t)
457{
458 return t != NULL && t->magic == THREAD_MAGIC;
459}
460
461/* Does basic initialization of T as a blocked thread named
462 NAME. */
463static void
464init_thread (struct thread *t, const char *name, int priority)
465{
466 ASSERT (t != NULL);
467 ASSERT (PRI_MIN <= priority && priority <= PRI_MAX);
468 ASSERT (name != NULL);
469
470 memset (t, 0, sizeof *t);
471 t->status = THREAD_BLOCKED;
472 strlcpy (t->name, name, sizeof t->name);
473 t->stack = (uint8_t *) t + PGSIZE;
474 t->priority = priority;
475 t->magic = THREAD_MAGIC;
476 list_push_back (&all_list, &t->allelem);
477#ifdef USERPROG
478 list_init(&t->children);
479#endif
480}
481
482/* Allocates a SIZE-byte frame at the top of thread T's stack and
483 returns a pointer to the frame's base. */
484static void *
485alloc_frame (struct thread *t, size_t size)
486{
487 /* Stack data is always allocated in word-size units. */
488 ASSERT (is_thread (t));
489 ASSERT (size % sizeof (uint32_t) == 0);
490
491 t->stack -= size;
492 return t->stack;
493}
494
495/* Chooses and returns the next thread to be scheduled. Should
496 return a thread from the run queue, unless the run queue is
497 empty. (If the running thread can continue running, then it
498 will be in the run queue.) If the run queue is empty, return
499 idle_thread. */
500static struct thread *
501next_thread_to_run (void)
502{
503 if (list_empty (&ready_list))
504 return idle_thread;
505 else
506 return list_entry (list_pop_front (&ready_list), struct thread, elem);
507}
508
509/* Completes a thread switch by activating the new thread's page
510 tables, and, if the previous thread is dying, destroying it.
511
512 At this function's invocation, we just switched from thread
513 PREV, the new thread is already running, and interrupts are
514 still disabled. This function is normally invoked by
515 thread_schedule() as its final action before returning, but
516 the first time a thread is scheduled it is called by
517 switch_entry() (see switch.S).
518
519 It's not safe to call printf() until the thread switch is
520 complete. In practice that means that printf()s should be
521 added at the end of the function.
522
523 After this function and its caller returns, the thread switch
524 is complete. */
525void
526thread_schedule_tail (struct thread *prev)
527{
528 struct thread *cur = running_thread ();
529
530 ASSERT (intr_get_level () == INTR_OFF);
531
532 /* Mark us as running. */
533 cur->status = THREAD_RUNNING;
534
535 /* Start new time slice. */
536 thread_ticks = 0;
537
538#ifdef USERPROG
539 /* Activate the new address space. */
540 process_activate ();
541#endif
542
543 /* If the thread we switched from is dying, destroy its struct
544 thread. This must happen late so that thread_exit() doesn't
545 pull out the rug under itself. (We don't free
546 initial_thread because its memory was not obtained via
547 palloc().) */
548 if (prev != NULL && prev->status == THREAD_DYING && prev != initial_thread)
549 {
550 ASSERT (prev != cur);
551 palloc_free_page (prev);
552 }
553}
554
555/* Schedules a new process. At entry, interrupts must be off and
556 the running process's state must have been changed from
557 running to some other state. This function finds another
558 thread to run and switches to it.
559
560 It's not safe to call printf() until thread_schedule_tail()
561 has completed. */
562static void
563schedule (void)
564{
565 struct thread *cur = running_thread ();
566 struct thread *next = next_thread_to_run ();
567 struct thread *prev = NULL;
568
569 ASSERT (intr_get_level () == INTR_OFF);
570 ASSERT (cur->status != THREAD_RUNNING);
571 ASSERT (is_thread (next));
572
573 if (cur != next)
574 prev = switch_threads (cur, next);
575 thread_schedule_tail (prev);
576}
577
578/* Returns a tid to use for a new thread. */
579static tid_t
580allocate_tid (void)
581{
582 static tid_t next_tid = 1;
583 tid_t tid;
584
585 lock_acquire (&tid_lock);
586 tid = next_tid++;
587 lock_release (&tid_lock);
588
589 return tid;
590}
591
592/* Offset of `stack' member within `struct thread'.
593 Used by switch.S, which can't figure it out on its own. */
594uint32_t thread_stack_ofs = offsetof (struct thread, stack);
diff --git a/pintos-progos/threads/thread.h b/pintos-progos/threads/thread.h
new file mode 100644
index 0000000..eebf42c
--- /dev/null
+++ b/pintos-progos/threads/thread.h
@@ -0,0 +1,144 @@
1#ifndef THREADS_THREAD_H
2#define THREADS_THREAD_H
3
4#include <debug.h>
5#include <list.h>
6#include <stdint.h>
7#include "threads/synch.h"
8
9/* States in a thread's life cycle. */
10enum thread_status
11 {
12 THREAD_RUNNING, /* Running thread. */
13 THREAD_READY, /* Not running but ready to run. */
14 THREAD_BLOCKED, /* Waiting for an event to trigger. */
15 THREAD_DYING /* About to be destroyed. */
16 };
17
18/* Thread identifier type.
19 You can redefine this to whatever type you like. */
20typedef int tid_t;
21#define TID_ERROR ((tid_t) -1) /* Error value for tid_t. */
22
23/* Thread priorities. */
24#define PRI_MIN 0 /* Lowest priority. */
25#define PRI_DEFAULT 31 /* Default priority. */
26#define PRI_MAX 63 /* Highest priority. */
27
28/* A kernel thread or user process.
29
30 Each thread structure is stored in its own 4 kB page. The
31 thread structure itself sits at the very bottom of the page
32 (at offset 0). The rest of the page is reserved for the
33 thread's kernel stack, which grows downward from the top of
34 the page (at offset 4 kB). Here's an illustration:
35
36 4 kB +---------------------------------+
37 | kernel stack |
38 | | |
39 | | |
40 | V |
41 | grows downward |
42 | |
43 | |
44 | |
45 | |
46 | |
47 | |
48 | |
49 | |
50 +---------------------------------+
51 | magic |
52 | : |
53 | : |
54 | name |
55 | status |
56 0 kB +---------------------------------+
57
58 The upshot of this is twofold:
59
60 1. First, `struct thread' must not be allowed to grow too
61 big. If it does, then there will not be enough room for
62 the kernel stack. Our base `struct thread' is only a
63 few bytes in size. It probably should stay well under 1
64 kB.
65
66 2. Second, kernel stacks must not be allowed to grow too
67 large. If a stack overflows, it will corrupt the thread
68 state. Thus, kernel functions should not allocate large
69 structures or arrays as non-static local variables. Use
70 dynamic allocation with malloc() or palloc_get_page()
71 instead.
72
73 The first symptom of either of these problems will probably be
74 an assertion failure in thread_current(), which checks that
75 the `magic' member of the running thread's `struct thread' is
76 set to THREAD_MAGIC. Stack overflow will normally change this
77 value, triggering the assertion. */
78/* The `elem' member has a dual purpose. It can be an element in
79 the run queue (thread.c), or it can be an element in a
80 semaphore wait list (synch.c). It can be used these two ways
81 only because they are mutually exclusive: only a thread in the
82 ready state is on the run queue, whereas only a thread in the
83 blocked state is on a semaphore wait list. */
84struct thread
85 {
86 /* Owned by thread.c. */
87 tid_t tid; /* Thread identifier. */
88 enum thread_status status; /* Thread state. */
89 char name[16]; /* Name (for debugging purposes). */
90 uint8_t *stack; /* Saved stack pointer. */
91 int priority; /* Priority. */
92 struct list_elem allelem; /* List element for all threads list. */
93
94 /* Shared between thread.c and synch.c. */
95 struct list_elem elem; /* List element. */
96
97#ifdef USERPROG
98 /* Owned by userprog/process.c */
99 struct process* process; /* Process Structure */
100 struct list children; /* Threads can hold processes, but not vice versa */
101 uint32_t *pagedir; /* Page directory. */
102#endif
103
104 /* Owned by thread.c. */
105 unsigned magic; /* Detects stack overflow. */
106 };
107
108/* If false (default), use round-robin scheduler.
109 If true, use multi-level feedback queue scheduler.
110 Controlled by kernel command-line option "-o mlfqs". */
111extern bool thread_mlfqs;
112
113void thread_init (void);
114void thread_start (void);
115
116void thread_tick (void);
117void thread_print_stats (void);
118
119typedef void thread_func (void *aux);
120tid_t thread_create (const char *name, int priority, thread_func *, void *);
121
122void thread_block (void);
123void thread_unblock (struct thread *);
124
125struct thread *thread_current (void);
126tid_t thread_tid (void);
127const char *thread_name (void);
128
129void thread_exit (void) NO_RETURN;
130void thread_yield (void);
131
132/* Performs some operation on thread t, given auxiliary data AUX. */
133typedef void thread_action_func (struct thread *t, void *aux);
134void thread_foreach (thread_action_func *, void *);
135
136int thread_get_priority (void);
137void thread_set_priority (int);
138
139int thread_get_nice (void);
140void thread_set_nice (int);
141int thread_get_recent_cpu (void);
142int thread_get_load_avg (void);
143
144#endif /* threads/thread.h */
diff --git a/pintos-progos/threads/vaddr.h b/pintos-progos/threads/vaddr.h
new file mode 100644
index 0000000..184c824
--- /dev/null
+++ b/pintos-progos/threads/vaddr.h
@@ -0,0 +1,89 @@
1#ifndef THREADS_VADDR_H
2#define THREADS_VADDR_H
3
4#include <debug.h>
5#include <stdint.h>
6#include <stdbool.h>
7
8#include "threads/loader.h"
9
10/* Functions and macros for working with virtual addresses.
11
12 See pte.h for functions and macros specifically for x86
13 hardware page tables. */
14
15#define BITMASK(SHIFT, CNT) (((1ul << (CNT)) - 1) << (SHIFT))
16
17/* Page offset (bits 0:12). */
18#define PGSHIFT 0 /* Index of first offset bit. */
19#define PGBITS 12 /* Number of offset bits. */
20#define PGSIZE (1 << PGBITS) /* Bytes in a page. */
21#define PGMASK BITMASK(PGSHIFT, PGBITS) /* Page offset bits (0:12). */
22
23/* Offset within a page. */
24static inline unsigned pg_ofs (const void *va) {
25 return (uintptr_t) va & PGMASK;
26}
27
28/* Virtual page number. */
29static inline uintptr_t pg_no (const void *va) {
30 return (uintptr_t) va >> PGBITS;
31}
32
33/* Round up to nearest page boundary. */
34static inline void *pg_round_up (const void *va) {
35 return (void *) (((uintptr_t) va + PGSIZE - 1) & ~PGMASK);
36}
37
38/* Round down to nearest page boundary. */
39static inline void *pg_round_down (const void *va) {
40 return (void *) ((uintptr_t) va & ~PGMASK);
41}
42
43/* Base address of the 1:1 physical-to-virtual mapping. Physical
44 memory is mapped starting at this virtual address. Thus,
45 physical address 0 is accessible at PHYS_BASE, physical
46 address address 0x1234 at (uint8_t *) PHYS_BASE + 0x1234, and
47 so on.
48
49 This address also marks the end of user programs' address
50 space. Up to this point in memory, user programs are allowed
51 to map whatever they like. At this point and above, the
52 virtual address space belongs to the kernel. */
53#define PHYS_BASE ((void *) LOADER_PHYS_BASE)
54
55/* Returns true if VADDR is a user virtual address. */
56static inline bool
57is_user_vaddr (const void *vaddr)
58{
59 return vaddr < PHYS_BASE;
60}
61
62/* Returns true if VADDR is a kernel virtual address. */
63static inline bool
64is_kernel_vaddr (const void *vaddr)
65{
66 return vaddr >= PHYS_BASE;
67}
68
69/* Returns kernel virtual address at which physical address PADDR
70 is mapped. */
71static inline void *
72ptov (uintptr_t paddr)
73{
74 ASSERT ((void *) paddr < PHYS_BASE);
75
76 return (void *) (paddr + PHYS_BASE);
77}
78
79/* Returns physical address at which kernel virtual address VADDR
80 is mapped. */
81static inline uintptr_t
82vtop (const void *vaddr)
83{
84 ASSERT (is_kernel_vaddr (vaddr));
85
86 return (uintptr_t) vaddr - (uintptr_t) PHYS_BASE;
87}
88
89#endif /* threads/vaddr.h */
diff --git a/pintos-progos/userprog/.gitignore b/pintos-progos/userprog/.gitignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/pintos-progos/userprog/.gitignore
@@ -0,0 +1,3 @@
1build
2bochsrc.txt
3bochsout.txt
diff --git a/pintos-progos/userprog/Make.vars b/pintos-progos/userprog/Make.vars
new file mode 100644
index 0000000..e4dbb08
--- /dev/null
+++ b/pintos-progos/userprog/Make.vars
@@ -0,0 +1,7 @@
1# -*- makefile -*-
2
3kernel.bin: DEFINES = -DUSERPROG -DFILESYS
4KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys
5TEST_SUBDIRS = tests/userprog tests/userprog/no-vm tests/filesys/base
6GRADING_FILE = $(SRCDIR)/tests/userprog/Grading
7SIMULATOR = --qemu
diff --git a/pintos-progos/userprog/Makefile b/pintos-progos/userprog/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/pintos-progos/userprog/Makefile
@@ -0,0 +1 @@
include ../Makefile.kernel
diff --git a/pintos-progos/userprog/exception.c b/pintos-progos/userprog/exception.c
new file mode 100644
index 0000000..17620ad
--- /dev/null
+++ b/pintos-progos/userprog/exception.c
@@ -0,0 +1,174 @@
1#include "userprog/exception.h"
2#include <inttypes.h>
3#include <stdio.h>
4#include "userprog/gdt.h"
5#include "threads/interrupt.h"
6#include "threads/thread.h"
7#include "threads/vaddr.h"
8
9/* Number of page faults processed. */
10static long long page_fault_cnt;
11
12static void kill (struct intr_frame *);
13static void page_fault (struct intr_frame *);
14
15/* Registers handlers for interrupts that can be caused by user
16 programs.
17
18 In a real Unix-like OS, most of these interrupts would be
19 passed along to the user process in the form of signals, as
20 described in [SV-386] 3-24 and 3-25, but we don't implement
21 signals. Instead, we'll make them simply kill the user
22 process.
23
24 Page faults are an exception. Here they are treated the same
25 way as other exceptions, but this will need to change to
26 implement virtual memory.
27
28 Refer to [IA32-v3a] section 5.15 "Exception and Interrupt
29 Reference" for a description of each of these exceptions. */
30void
31exception_init (void)
32{
33 /* These exceptions can be raised explicitly by a user program,
34 e.g. via the INT, INT3, INTO, and BOUND instructions. Thus,
35 we set DPL==3, meaning that user programs are allowed to
36 invoke them via these instructions. */
37 intr_register_int (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
38 intr_register_int (4, 3, INTR_ON, kill, "#OF Overflow Exception");
39 intr_register_int (5, 3, INTR_ON, kill,
40 "#BR BOUND Range Exceeded Exception");
41
42 /* These exceptions have DPL==0, preventing user processes from
43 invoking them via the INT instruction. They can still be
44 caused indirectly, e.g. #DE can be caused by dividing by
45 0. */
46 intr_register_int (0, 0, INTR_ON, kill, "#DE Divide Error");
47 intr_register_int (1, 0, INTR_ON, kill, "#DB Debug Exception");
48 intr_register_int (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
49 intr_register_int (7, 0, INTR_ON, kill,
50 "#NM Device Not Available Exception");
51 intr_register_int (11, 0, INTR_ON, kill, "#NP Segment Not Present");
52 intr_register_int (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
53 intr_register_int (13, 0, INTR_ON, kill, "#GP General Protection Exception");
54 intr_register_int (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
55 intr_register_int (19, 0, INTR_ON, kill,
56 "#XF SIMD Floating-Point Exception");
57
58 /* Most exceptions can be handled with interrupts turned on.
59 We need to disable interrupts for page faults because the
60 fault address is stored in CR2 and needs to be preserved. */
61 intr_register_int (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception");
62}
63
64/* Prints exception statistics. */
65void
66exception_print_stats (void)
67{
68 printf ("Exception: %lld page faults\n", page_fault_cnt);
69}
70
71/* Handler for an exception (probably) caused by a user process. */
72static void
73kill (struct intr_frame *f)
74{
75 /* This interrupt is one (probably) caused by a user process.
76 For example, the process might have tried to access unmapped
77 virtual memory (a page fault). For now, we simply kill the
78 user process. Later, we'll want to handle page faults in
79 the kernel. Real Unix-like operating systems pass most
80 exceptions back to the process via signals, but we don't
81 implement them. */
82
83 /* The interrupt frame's code segment value tells us where the
84 exception originated. */
85 switch (f->cs)
86 {
87 case SEL_UCSEG:
88 /* User's code segment, so it's a user exception, as we
89 expected. Kill the user process. */
90 printf ("%s: dying due to interrupt %#04x (%s).\n",
91 thread_name (), f->vec_no, intr_name (f->vec_no));
92 intr_dump_frame (f);
93 thread_exit ();
94
95 case SEL_KCSEG:
96 /* Kernel's code segment, which indicates a kernel bug.
97 Kernel code shouldn't throw exceptions. (Page faults
98 may cause kernel exceptions--but they shouldn't arrive
99 here.) Panic the kernel to make the point. */
100 intr_dump_frame (f);
101 PANIC ("Kernel bug - unexpected interrupt in kernel");
102
103 default:
104 /* Some other code segment? Shouldn't happen. Panic the
105 kernel. */
106 printf ("Interrupt %#04x (%s) in unknown segment %04x\n",
107 f->vec_no, intr_name (f->vec_no), f->cs);
108 thread_exit ();
109 }
110}
111
112/* Page fault handler. This is a skeleton that must be filled in
113 to implement virtual memory. Some solutions to project 2 may
114 also require modifying this code.
115
116 At entry, the address that faulted is in CR2 (Control Register
117 2) and information about the fault, formatted as described in
118 the PF_* macros in exception.h, is in F's error_code member. The
119 example code here shows how to parse that information. You
120 can find more information about both of these in the
121 description of "Interrupt 14--Page Fault Exception (#PF)" in
122 [IA32-v3a] section 5.15 "Exception and Interrupt Reference". */
123static void
124page_fault (struct intr_frame *f)
125{
126 bool not_present; /* True: not-present page, false: writing r/o page. */
127 bool write; /* True: access was write, false: access was read. */
128 bool user; /* True: access by user, false: access by kernel. */
129 void *fault_addr; /* Fault address. */
130
131 /* Obtain faulting address, the virtual address that was
132 accessed to cause the fault. It may point to code or to
133 data. It is not necessarily the address of the instruction
134 that caused the fault (that's f->eip).
135 See [IA32-v2a] "MOV--Move to/from Control Registers" and
136 [IA32-v3a] 5.15 "Interrupt 14--Page Fault Exception
137 (#PF)". */
138 asm ("movl %%cr2, %0" : "=r" (fault_addr));
139
140 /* Turn interrupts back on (they were only off so that we could
141 be assured of reading CR2 before it changed). */
142 intr_enable ();
143
144 /* Count page faults. */
145 page_fault_cnt++;
146
147 /* Determine cause. */
148 not_present = (f->error_code & PF_P) == 0;
149 write = (f->error_code & PF_W) != 0;
150 user = (f->error_code & PF_U) != 0;
151
152 /* To implement virtual memory, adapt the rest of the function
153 body, adding code that brings in the page to
154 which fault_addr refers. */
155 if (is_user_vaddr(fault_addr)) {
156 if (! user) {
157 /* syscall exception; set eax and eip */
158 f->eip = (void*)f->eax;
159 f->eax = 0xFFFFFFFF;
160 return;
161 } else {
162 /* user process access violation */
163 thread_exit();
164 }
165 } else {
166 printf ("Page fault at %p: %s error %s page in %s context.\n",
167 fault_addr,
168 not_present ? "not present" : "rights violation",
169 write ? "writing" : "reading",
170 user ? "user" : "kernel");
171 kill (f);
172 }
173}
174
diff --git a/pintos-progos/userprog/exception.h b/pintos-progos/userprog/exception.h
new file mode 100644
index 0000000..f83e615
--- /dev/null
+++ b/pintos-progos/userprog/exception.h
@@ -0,0 +1,12 @@
1#ifndef USERPROG_EXCEPTION_H
2#define USERPROG_EXCEPTION_H
3
4/* Page fault error code bits that describe the cause of the exception. */
5#define PF_P 0x1 /* 0: not-present page. 1: access rights violation. */
6#define PF_W 0x2 /* 0: read, 1: write. */
7#define PF_U 0x4 /* 0: kernel, 1: user process. */
8
9void exception_init (void);
10void exception_print_stats (void);
11
12#endif /* userprog/exception.h */
diff --git a/pintos-progos/userprog/gdt.c b/pintos-progos/userprog/gdt.c
new file mode 100644
index 0000000..e866037
--- /dev/null
+++ b/pintos-progos/userprog/gdt.c
@@ -0,0 +1,146 @@
1#include "userprog/gdt.h"
2#include <debug.h>
3#include "userprog/tss.h"
4#include "threads/palloc.h"
5#include "threads/vaddr.h"
6
7/* The Global Descriptor Table (GDT).
8
9 The GDT, an x86-specific structure, defines segments that can
10 potentially be used by all processes in a system, subject to
11 their permissions. There is also a per-process Local
12 Descriptor Table (LDT) but that is not used by modern
13 operating systems.
14
15 Each entry in the GDT, which is known by its byte offset in
16 the table, identifies a segment. For our purposes only three
17 types of segments are of interest: code, data, and TSS or
18 Task-State Segment descriptors. The former two types are
19 exactly what they sound like. The TSS is used primarily for
20 stack switching on interrupts.
21
22 For more information on the GDT as used here, refer to
23 [IA32-v3a] 3.2 "Using Segments" through 3.5 "System Descriptor
24 Types". */
25static uint64_t gdt[SEL_CNT];
26
27/* GDT helpers. */
28static uint64_t make_code_desc (int dpl);
29static uint64_t make_data_desc (int dpl);
30static uint64_t make_tss_desc (void *laddr);
31static uint64_t make_gdtr_operand (uint16_t limit, void *base);
32
33/* Sets up a proper GDT. The bootstrap loader's GDT didn't
34 include user-mode selectors or a TSS, but we need both now. */
35void
36gdt_init (void)
37{
38 uint64_t gdtr_operand;
39
40 /* Initialize GDT. */
41 gdt[SEL_NULL / sizeof *gdt] = 0;
42 gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0);
43 gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0);
44 gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3);
45 gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3);
46 gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ());
47
48 /* Load GDTR, TR. See [IA32-v3a] 2.4.1 "Global Descriptor
49 Table Register (GDTR)", 2.4.4 "Task Register (TR)", and
50 6.2.4 "Task Register". */
51 gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt);
52 asm volatile ("lgdt %0" : : "m" (gdtr_operand));
53 asm volatile ("ltr %w0" : : "q" (SEL_TSS));
54}
55
56/* System segment or code/data segment? */
57enum seg_class
58 {
59 CLS_SYSTEM = 0, /* System segment. */
60 CLS_CODE_DATA = 1 /* Code or data segment. */
61 };
62
63/* Limit has byte or 4 kB page granularity? */
64enum seg_granularity
65 {
66 GRAN_BYTE = 0, /* Limit has 1-byte granularity. */
67 GRAN_PAGE = 1 /* Limit has 4 kB granularity. */
68 };
69
70/* Returns a segment descriptor with the given 32-bit BASE and
71 20-bit LIMIT (whose interpretation depends on GRANULARITY).
72 The descriptor represents a system or code/data segment
73 according to CLASS, and TYPE is its type (whose interpretation
74 depends on the class).
75
76 The segment has descriptor privilege level DPL, meaning that
77 it can be used in rings numbered DPL or lower. In practice,
78 DPL==3 means that user processes can use the segment and
79 DPL==0 means that only the kernel can use the segment. See
80 [IA32-v3a] 4.5 "Privilege Levels" for further discussion. */
81static uint64_t
82make_seg_desc (uint32_t base,
83 uint32_t limit,
84 enum seg_class class,
85 int type,
86 int dpl,
87 enum seg_granularity granularity)
88{
89 uint32_t e0, e1;
90
91 ASSERT (limit <= 0xfffff);
92 ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA);
93 ASSERT (type >= 0 && type <= 15);
94 ASSERT (dpl >= 0 && dpl <= 3);
95 ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE);
96
97 e0 = ((limit & 0xffff) /* Limit 15:0. */
98 | (base << 16)); /* Base 15:0. */
99
100 e1 = (((base >> 16) & 0xff) /* Base 23:16. */
101 | (type << 8) /* Segment type. */
102 | (class << 12) /* 0=system, 1=code/data. */
103 | (dpl << 13) /* Descriptor privilege. */
104 | (1 << 15) /* Present. */
105 | (limit & 0xf0000) /* Limit 16:19. */
106 | (1 << 22) /* 32-bit segment. */
107 | (granularity << 23) /* Byte/page granularity. */
108 | (base & 0xff000000)); /* Base 31:24. */
109
110 return e0 | ((uint64_t) e1 << 32);
111}
112
113/* Returns a descriptor for a readable code segment with base at
114 0, a limit of 4 GB, and the given DPL. */
115static uint64_t
116make_code_desc (int dpl)
117{
118 return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE);
119}
120
121/* Returns a descriptor for a writable data segment with base at
122 0, a limit of 4 GB, and the given DPL. */
123static uint64_t
124make_data_desc (int dpl)
125{
126 return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE);
127}
128
129/* Returns a descriptor for an "available" 32-bit Task-State
130 Segment with its base at the given linear address, a limit of
131 0x67 bytes (the size of a 32-bit TSS), and a DPL of 0.
132 See [IA32-v3a] 6.2.2 "TSS Descriptor". */
133static uint64_t
134make_tss_desc (void *laddr)
135{
136 return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE);
137}
138
139
140/* Returns a descriptor that yields the given LIMIT and BASE when
141 used as an operand for the LGDT instruction. */
142static uint64_t
143make_gdtr_operand (uint16_t limit, void *base)
144{
145 return limit | ((uint64_t) (uint32_t) base << 16);
146}
diff --git a/pintos-progos/userprog/gdt.h b/pintos-progos/userprog/gdt.h
new file mode 100644
index 0000000..81fe50c
--- /dev/null
+++ b/pintos-progos/userprog/gdt.h
@@ -0,0 +1,15 @@
1#ifndef USERPROG_GDT_H
2#define USERPROG_GDT_H
3
4#include "threads/loader.h"
5
6/* Segment selectors.
7 More selectors are defined by the loader in loader.h. */
8#define SEL_UCSEG 0x1B /* User code selector. */
9#define SEL_UDSEG 0x23 /* User data selector. */
10#define SEL_TSS 0x28 /* Task-state segment. */
11#define SEL_CNT 6 /* Number of segments. */
12
13void gdt_init (void);
14
15#endif /* userprog/gdt.h */
diff --git a/pintos-progos/userprog/pagedir.c b/pintos-progos/userprog/pagedir.c
new file mode 100644
index 0000000..a6a87b8
--- /dev/null
+++ b/pintos-progos/userprog/pagedir.c
@@ -0,0 +1,263 @@
1#include "userprog/pagedir.h"
2#include <stdbool.h>
3#include <stddef.h>
4#include <string.h>
5#include "threads/init.h"
6#include "threads/pte.h"
7#include "threads/palloc.h"
8
9static uint32_t *active_pd (void);
10static void invalidate_pagedir (uint32_t *);
11
12/* Creates a new page directory that has mappings for kernel
13 virtual addresses, but none for user virtual addresses.
14 Returns the new page directory, or a null pointer if memory
15 allocation fails. */
16uint32_t *
17pagedir_create (void)
18{
19 uint32_t *pd = palloc_get_page (0);
20 if (pd != NULL)
21 memcpy (pd, init_page_dir, PGSIZE);
22 return pd;
23}
24
25/* Destroys page directory PD, freeing all the pages it
26 references. */
27void
28pagedir_destroy (uint32_t *pd)
29{
30 uint32_t *pde;
31
32 if (pd == NULL)
33 return;
34
35 ASSERT (pd != init_page_dir);
36 for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++)
37 if (*pde & PTE_P)
38 {
39 uint32_t *pt = pde_get_pt (*pde);
40 uint32_t *pte;
41
42 for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
43 if (*pte & PTE_P)
44 palloc_free_page (pte_get_page (*pte));
45 palloc_free_page (pt);
46 }
47 palloc_free_page (pd);
48}
49
50/* Returns the address of the page table entry for virtual
51 address VADDR in page directory PD.
52 If PD does not have a page table for VADDR, behavior depends
53 on CREATE. If CREATE is true, then a new page table is
54 created and a pointer into it is returned. Otherwise, a null
55 pointer is returned. */
56static uint32_t *
57lookup_page (uint32_t *pd, const void *vaddr, bool create)
58{
59 uint32_t *pt, *pde;
60
61 ASSERT (pd != NULL);
62
63 /* Shouldn't create new kernel virtual mappings. */
64 ASSERT (!create || is_user_vaddr (vaddr));
65
66 /* Check for a page table for VADDR.
67 If one is missing, create one if requested. */
68 pde = pd + pd_no (vaddr);
69 if (*pde == 0)
70 {
71 if (create)
72 {
73 pt = palloc_get_page (PAL_ZERO);
74 if (pt == NULL)
75 return NULL;
76
77 *pde = pde_create (pt);
78 }
79 else
80 return NULL;
81 }
82
83 /* Return the page table entry. */
84 pt = pde_get_pt (*pde);
85 return &pt[pt_no (vaddr)];
86}
87
88/* Adds a mapping in page directory PD from user virtual page
89 UPAGE to the physical frame identified by kernel virtual
90 address KPAGE.
91 UPAGE must not already be mapped.
92 KPAGE should probably be a page obtained from the user pool
93 with palloc_get_page().
94 If WRITABLE is true, the new page is read/write;
95 otherwise it is read-only.
96 Returns true if successful, false if memory allocation
97 failed. */
98bool
99pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool writable)
100{
101 uint32_t *pte;
102
103 ASSERT (pg_ofs (upage) == 0);
104 ASSERT (pg_ofs (kpage) == 0);
105 ASSERT (is_user_vaddr (upage));
106 ASSERT (vtop (kpage) >> PTSHIFT < init_ram_pages);
107 ASSERT (pd != init_page_dir);
108
109 pte = lookup_page (pd, upage, true);
110
111 if (pte != NULL)
112 {
113 ASSERT ((*pte & PTE_P) == 0);
114 *pte = pte_create_user (kpage, writable);
115 return true;
116 }
117 else
118 return false;
119}
120
121/* Looks up the physical address that corresponds to user virtual
122 address UADDR in PD. Returns the kernel virtual address
123 corresponding to that physical address, or a null pointer if
124 UADDR is unmapped. */
125void *
126pagedir_get_page (uint32_t *pd, const void *uaddr)
127{
128 uint32_t *pte;
129
130 ASSERT (is_user_vaddr (uaddr));
131
132 pte = lookup_page (pd, uaddr, false);
133 if (pte != NULL && (*pte & PTE_P) != 0)
134 return pte_get_page (*pte) + pg_ofs (uaddr);
135 else
136 return NULL;
137}
138
139/* Marks user virtual page UPAGE "not present" in page
140 directory PD. Later accesses to the page will fault. Other
141 bits in the page table entry are preserved.
142 UPAGE need not be mapped. */
143void
144pagedir_clear_page (uint32_t *pd, void *upage)
145{
146 uint32_t *pte;
147
148 ASSERT (pg_ofs (upage) == 0);
149 ASSERT (is_user_vaddr (upage));
150
151 pte = lookup_page (pd, upage, false);
152 if (pte != NULL && (*pte & PTE_P) != 0)
153 {
154 *pte &= ~PTE_P;
155 invalidate_pagedir (pd);
156 }
157}
158
159/* Returns true if the PTE for virtual page VPAGE in PD is dirty,
160 that is, if the page has been modified since the PTE was
161 installed.
162 Returns false if PD contains no PTE for VPAGE. */
163bool
164pagedir_is_dirty (uint32_t *pd, const void *vpage)
165{
166 uint32_t *pte = lookup_page (pd, vpage, false);
167 return pte != NULL && (*pte & PTE_D) != 0;
168}
169
170/* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
171 in PD. */
172void
173pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty)
174{
175 uint32_t *pte = lookup_page (pd, vpage, false);
176 if (pte != NULL)
177 {
178 if (dirty)
179 *pte |= PTE_D;
180 else
181 {
182 *pte &= ~(uint32_t) PTE_D;
183 invalidate_pagedir (pd);
184 }
185 }
186}
187
188/* Returns true if the PTE for virtual page VPAGE in PD has been
189 accessed recently, that is, between the time the PTE was
190 installed and the last time it was cleared. Returns false if
191 PD contains no PTE for VPAGE. */
192bool
193pagedir_is_accessed (uint32_t *pd, const void *vpage)
194{
195 uint32_t *pte = lookup_page (pd, vpage, false);
196 return pte != NULL && (*pte & PTE_A) != 0;
197}
198
199/* Sets the accessed bit to ACCESSED in the PTE for virtual page
200 VPAGE in PD. */
201void
202pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed)
203{
204 uint32_t *pte = lookup_page (pd, vpage, false);
205 if (pte != NULL)
206 {
207 if (accessed)
208 *pte |= PTE_A;
209 else
210 {
211 *pte &= ~(uint32_t) PTE_A;
212 invalidate_pagedir (pd);
213 }
214 }
215}
216
217/* Loads page directory PD into the CPU's page directory base
218 register. */
219void
220pagedir_activate (uint32_t *pd)
221{
222 if (pd == NULL)
223 pd = init_page_dir;
224
225 /* Store the physical address of the page directory into CR3
226 aka PDBR (page directory base register). This activates our
227 new page tables immediately. See [IA32-v2a] "MOV--Move
228 to/from Control Registers" and [IA32-v3a] 3.7.5 "Base
229 Address of the Page Directory". */
230 asm volatile ("movl %0, %%cr3" : : "r" (vtop (pd)) : "memory");
231}
232
233/* Returns the currently active page directory. */
234static uint32_t *
235active_pd (void)
236{
237 /* Copy CR3, the page directory base register (PDBR), into
238 `pd'.
239 See [IA32-v2a] "MOV--Move to/from Control Registers" and
240 [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */
241 uintptr_t pd;
242 asm volatile ("movl %%cr3, %0" : "=r" (pd));
243 return ptov (pd);
244}
245
246/* Seom page table changes can cause the CPU's translation
247 lookaside buffer (TLB) to become out-of-sync with the page
248 table. When this happens, we have to "invalidate" the TLB by
249 re-activating it.
250
251 This function invalidates the TLB if PD is the active page
252 directory. (If PD is not active then its entries are not in
253 the TLB, so there is no need to invalidate anything.) */
254static void
255invalidate_pagedir (uint32_t *pd)
256{
257 if (active_pd () == pd)
258 {
259 /* Re-activating PD clears the TLB. See [IA32-v3a] 3.12
260 "Translation Lookaside Buffers (TLBs)". */
261 pagedir_activate (pd);
262 }
263}
diff --git a/pintos-progos/userprog/pagedir.h b/pintos-progos/userprog/pagedir.h
new file mode 100644
index 0000000..cd92447
--- /dev/null
+++ b/pintos-progos/userprog/pagedir.h
@@ -0,0 +1,18 @@
1#ifndef USERPROG_PAGEDIR_H
2#define USERPROG_PAGEDIR_H
3
4#include <stdbool.h>
5#include <stdint.h>
6
7uint32_t *pagedir_create (void);
8void pagedir_destroy (uint32_t *pd);
9bool pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool rw);
10void *pagedir_get_page (uint32_t *pd, const void *upage);
11void pagedir_clear_page (uint32_t *pd, void *upage);
12bool pagedir_is_dirty (uint32_t *pd, const void *upage);
13void pagedir_set_dirty (uint32_t *pd, const void *upage, bool dirty);
14bool pagedir_is_accessed (uint32_t *pd, const void *upage);
15void pagedir_set_accessed (uint32_t *pd, const void *upage, bool accessed);
16void pagedir_activate (uint32_t *pd);
17
18#endif /* userprog/pagedir.h */
diff --git a/pintos-progos/userprog/process.c b/pintos-progos/userprog/process.c
new file mode 100644
index 0000000..adb6b66
--- /dev/null
+++ b/pintos-progos/userprog/process.c
@@ -0,0 +1,721 @@
1#include "userprog/process.h"
2#include <debug.h>
3#include <inttypes.h>
4#include <round.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include "userprog/gdt.h"
9#include "userprog/pagedir.h"
10#include "userprog/tss.h"
11#include "filesys/directory.h"
12#include "filesys/file.h"
13#include "filesys/filesys.h"
14#include "threads/flags.h"
15#include "threads/init.h"
16#include "threads/interrupt.h"
17#include "threads/palloc.h"
18#include "threads/synch.h"
19#include "threads/thread.h"
20#include "threads/vaddr.h"
21
22/* data structure to communicate with the thread initializing a new process */
23struct start_aux_data {
24 char *filename;
25 struct semaphore startup_sem;
26 struct thread *parent_thread;
27 struct process *new_process;
28};
29
30/* filesystem lock */
31struct lock filesys_lock;
32
33/* prototypes */
34static thread_func start_process NO_RETURN;
35static bool load (char *filename, void (**eip) (void), void **esp);
36static bool setup_stack (void **esp);
37static bool init_fd_table (struct fd_table * table);
38
39/* Initialize the filesystem lock */
40void
41process_init ()
42{
43 lock_init (&filesys_lock);
44}
45
46/* Get current process (only valid for processes) */
47struct process*
48process_current ()
49{
50 ASSERT (thread_current()->process != NULL);
51 return thread_current()->process;
52}
53
54/* Starts a new thread running a user program loaded from
55 `filename`.
56 The new thread may be scheduled (and may even exit)
57 before process_execute() returns. Returns the new process's
58 thread id, or TID_ERROR if the thread cannot be created.
59
60 In the first assignment, you should change this to function to
61
62 process_execute (const char *cmd)
63
64 and support command strings such as "echo A B C". You
65 will also need to change `load` and `setup_stack`. */
66tid_t
67process_execute (const char *filename)
68{
69 tid_t tid = TID_ERROR;
70 char *fn_copy = NULL;
71 struct start_aux_data *aux_data = NULL;
72
73 /* Setup the auxiliary data for starting up the new process */
74 fn_copy = palloc_get_page (0);
75 aux_data = palloc_get_page (0);
76 if (aux_data == NULL || fn_copy == NULL)
77 goto done;
78 strlcpy (fn_copy, filename, PGSIZE);
79 aux_data->filename = fn_copy;
80 aux_data->parent_thread = thread_current ();
81 aux_data->new_process = NULL;
82 sema_init (&aux_data->startup_sem, 0);
83
84 /* Create a new thread to execute FILE_NAME. */
85 tid = thread_create (fn_copy, PRI_DEFAULT, start_process, aux_data);
86 if (tid == TID_ERROR)
87 goto done;
88
89 /* wait for startup */
90 sema_down (&aux_data->startup_sem);
91 if (aux_data->new_process == NULL) {
92 tid = TID_ERROR;
93 goto done;
94 }
95 /* register child process */
96 list_push_back (&thread_current()->children,
97 &aux_data->new_process->parentelem);
98
99 done:
100 palloc_free_page (fn_copy);
101 palloc_free_page (aux_data);
102 return tid;
103}
104
105/* A thread function that loads a user process and starts it
106 running. */
107static void
108start_process (void *aux)
109{
110 struct intr_frame if_;
111 struct start_aux_data *aux_data = (struct start_aux_data*) aux;
112 struct thread *thread = thread_current ();
113
114 /* Initialize Process */
115 struct process *process = palloc_get_page (PAL_ZERO);
116 if (process == NULL)
117 goto signal;
118 sema_init (&process->exit_sem, 0);
119 lock_init (&process->exit_lock);
120 process->exit_status = -1;
121 if (! init_fd_table (&process->fd_table))
122 goto signal;
123
124 /* register process */
125 process->thread_id = thread->tid;
126 process->parent_tid = aux_data->parent_thread->tid;
127 thread->process = process;
128
129 /* Initialize interrupt frame and load executable. */
130 memset (&if_, 0, sizeof if_);
131 if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
132 if_.cs = SEL_UCSEG;
133 if_.eflags = FLAG_IF | FLAG_MBS;
134 if (! load (aux_data->filename, &if_.eip, &if_.esp)) {
135 thread->process = NULL;
136 } else {
137 aux_data->new_process = thread->process;
138 }
139
140 signal:
141 /* Signal the parent process that loading is finished */
142 sema_up (&aux_data->startup_sem); /* transfer ownership of aux_data */
143
144 /* If process startup failed, quit. */
145 if (thread->process == NULL) {
146 if (process != NULL) {
147 if (process->fd_table.fds != NULL)
148 palloc_free_page (process->fd_table.fds);
149 palloc_free_page (process);
150 }
151 thread_exit ();
152 }
153
154 /* Start the user process by simulating a return from an
155 interrupt, implemented by intr_exit (in
156 threads/intr-stubs.S). Because intr_exit takes all of its
157 arguments on the stack in the form of a `struct intr_frame',
158 we just point the stack pointer (%esp) to our stack frame
159 and jump to it. */
160 asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory");
161 NOT_REACHED ();
162}
163
164/* Waits for thread TID to die and returns its exit status. If
165 it was terminated by the kernel (i.e. killed due to an
166 exception), returns -1. If TID is invalid or if it was not a
167 child of the calling process, or if process_wait() has already
168 been successfully called for the given TID, returns -1
169 immediately, without waiting. */
170int
171process_wait (tid_t child_tid)
172{
173 struct thread *cur = thread_current ();
174 struct process *child = NULL;
175
176 /* iterate over child processes */
177 struct list_elem *e = list_head (&cur->children);
178 while ((e = list_next (e)) != list_end (&cur->children)) {
179 struct process* t = list_entry (e, struct process, parentelem);
180 if (t->thread_id == child_tid) {
181 list_remove (e);
182 child = t;
183 break;
184 }
185 }
186 if (child == NULL) {
187 return -1;
188 }
189 sema_down (&child->exit_sem);
190 int exit_status = child->exit_status;
191 palloc_free_page (child);
192 return exit_status;
193}
194
195/* Free the current process's resources. */
196void
197process_exit (void)
198{
199 struct thread *thread = thread_current ();
200 ASSERT (thread != NULL);
201
202 /* remove (and if necessary clean up) child processes */
203 struct list_elem *e = list_head (&thread->children);
204 while ((e = list_next (e)) != list_end (&thread->children)) {
205 struct process *p = list_entry (e, struct process, parentelem);
206 bool process_dying;
207 lock_acquire (&p->exit_lock);
208 process_dying = p->parent_tid < 0;
209 p->parent_tid = -1;
210 lock_release (&p->exit_lock);
211 if (process_dying)
212 palloc_free_page (p);
213 }
214
215 if (thread->process == NULL)
216 return; /* not a process, nothing else left to do */
217
218 struct process *proc = thread->process;
219 uint32_t *pd;
220
221 printf ("%s: exit(%d)\n", thread->name, proc->exit_status);
222
223 /* close executable, allow write */
224 if (proc->executable != NULL) {
225 lock_acquire (&filesys_lock);
226 file_close (proc->executable);
227 lock_release (&filesys_lock);
228 }
229
230 int fd;
231 for (fd = 2; fd <= proc->fd_table.fd_max; fd++) {
232 process_close_file (fd);
233 }
234 palloc_free_page (proc->fd_table.fds);
235
236 /* Destroy the current process's page directory and switch back
237 to the kernel-only page directory. */
238 pd = thread->pagedir;
239 if (pd != NULL) {
240 /* Correct ordering here is crucial. We must set
241 cur->pagedir to NULL before switching page directories,
242 so that a timer interrupt can't switch back to the
243 process page directory. We must activate the base page
244 directory before destroying the process's page
245 directory, or our active page directory will be one
246 that's been freed (and cleared). */
247 thread->pagedir = NULL;
248 pagedir_activate (NULL);
249 pagedir_destroy (pd);
250 }
251
252 /* Destroy the process structure if the parent is not alive
253 * any more. Atomic test and set would be sufficient here.
254 */
255 bool parent_dead = false;
256 lock_acquire (&proc->exit_lock);
257 parent_dead = proc->parent_tid < 0;
258 proc->parent_tid = -1;
259 lock_release (&proc->exit_lock);
260 if (parent_dead) {
261 palloc_free_page (proc);
262 } else {
263 sema_up (&proc->exit_sem);
264 }
265}
266
267/* Sets up the CPU for running user code in the current
268 thread.
269 This function is called on every context switch. */
270void
271process_activate (void)
272{
273 struct thread *t = thread_current ();
274 /* Activate thread's page tables. */
275 pagedir_activate (t->pagedir);
276
277 /* Set thread's kernel stack for use in processing
278 interrupts. */
279 tss_update ();
280}
281
282/* We load ELF binaries. The following definitions are taken
283 from the ELF specification, [ELF1], more-or-less verbatim. */
284
285/* ELF types. See [ELF1] 1-2. */
286typedef uint32_t Elf32_Word, Elf32_Addr, Elf32_Off;
287typedef uint16_t Elf32_Half;
288
289/* For use with ELF types in printf(). */
290#define PE32Wx PRIx32 /* Print Elf32_Word in hexadecimal. */
291#define PE32Ax PRIx32 /* Print Elf32_Addr in hexadecimal. */
292#define PE32Ox PRIx32 /* Print Elf32_Off in hexadecimal. */
293#define PE32Hx PRIx16 /* Print Elf32_Half in hexadecimal. */
294
295/* Executable header. See [ELF1] 1-4 to 1-8.
296 This appears at the very beginning of an ELF binary. */
297struct Elf32_Ehdr
298{
299 unsigned char e_ident[16];
300 Elf32_Half e_type;
301 Elf32_Half e_machine;
302 Elf32_Word e_version;
303 Elf32_Addr e_entry;
304 Elf32_Off e_phoff;
305 Elf32_Off e_shoff;
306 Elf32_Word e_flags;
307 Elf32_Half e_ehsize;
308 Elf32_Half e_phentsize;
309 Elf32_Half e_phnum;
310 Elf32_Half e_shentsize;
311 Elf32_Half e_shnum;
312 Elf32_Half e_shstrndx;
313};
314
315/* Program header. See [ELF1] 2-2 to 2-4.
316 There are e_phnum of these, starting at file offset e_phoff
317 (see [ELF1] 1-6). */
318struct Elf32_Phdr
319{
320 Elf32_Word p_type;
321 Elf32_Off p_offset;
322 Elf32_Addr p_vaddr;
323 Elf32_Addr p_paddr;
324 Elf32_Word p_filesz;
325 Elf32_Word p_memsz;
326 Elf32_Word p_flags;
327 Elf32_Word p_align;
328};
329
330/* Values for p_type. See [ELF1] 2-3. */
331#define PT_NULL 0 /* Ignore. */
332#define PT_LOAD 1 /* Loadable segment. */
333#define PT_DYNAMIC 2 /* Dynamic linking info. */
334#define PT_INTERP 3 /* Name of dynamic loader. */
335#define PT_NOTE 4 /* Auxiliary info. */
336#define PT_SHLIB 5 /* Reserved. */
337#define PT_PHDR 6 /* Program header table. */
338#define PT_STACK 0x6474e551 /* Stack segment. */
339
340/* Flags for p_flags. See [ELF3] 2-3 and 2-4. */
341#define PF_X 1 /* Executable. */
342#define PF_W 2 /* Writable. */
343#define PF_R 4 /* Readable. */
344
345static bool validate_segment (const struct Elf32_Phdr *, struct file *);
346static bool load_segment (struct file *file, off_t ofs, uint8_t *upage,
347 uint32_t read_bytes, uint32_t zero_bytes,
348 bool writable);
349
350/* Loads an ELF executable from file_name (the first word of
351 cmd) into the current thread.
352 Stores the executable's entry point into *EIP
353 and its initial stack pointer into *ESP.
354 Returns true if successful, false otherwise. */
355bool
356load (char *file_name, void (**eip) (void), void **esp)
357{
358 struct thread *t = thread_current ();
359 struct Elf32_Ehdr ehdr;
360 struct file *file = NULL;
361 off_t file_ofs;
362 bool success = false;
363 int i;
364
365 /* Allocate and activate page directory. */
366 t->pagedir = pagedir_create ();
367 if (t->pagedir == NULL)
368 return false;
369 process_activate ();
370
371 /* Coarse grained filesystem lock for loading */
372 lock_acquire (&filesys_lock);
373
374 /* Open executable file. */
375 file = filesys_open (file_name);
376 if (file == NULL)
377 goto done;
378
379 /* Deny writes to the file during loading */
380 file_deny_write (file);
381
382 /* Read and verify executable header. */
383 if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr
384 || memcmp (ehdr.e_ident, "\177ELF\1\1\1", 7)
385 || ehdr.e_type != 2
386 || ehdr.e_machine != 3
387 || ehdr.e_version != 1
388 || ehdr.e_phentsize != sizeof (struct Elf32_Phdr)
389 || ehdr.e_phnum > 1024)
390 {
391 printf ("load: %s: error loading executable\n", file_name);
392 goto done;
393 }
394
395 /* Read program headers. */
396 file_ofs = ehdr.e_phoff;
397 for (i = 0; i < ehdr.e_phnum; i++)
398 {
399 struct Elf32_Phdr phdr;
400
401 if (file_ofs < 0 || file_ofs > file_length (file))
402 goto done;
403 file_seek (file, file_ofs);
404
405 if (file_read (file, &phdr, sizeof phdr) != sizeof phdr)
406 goto done;
407 file_ofs += sizeof phdr;
408 if (phdr.p_vaddr < PGSIZE)
409 continue; /* Ignore build-id segment */
410 switch (phdr.p_type)
411 {
412 case PT_NULL:
413 case PT_NOTE:
414 case PT_PHDR:
415 case PT_STACK:
416 default:
417 /* Ignore this segment. */
418 break;
419 case PT_DYNAMIC:
420 case PT_INTERP:
421 case PT_SHLIB:
422 goto done;
423 case PT_LOAD:
424 if (phdr.p_vaddr == 0)
425 break; // Ignore the .note.gnu.build-i segment
426 if (validate_segment (&phdr, file))
427 {
428 bool writable = (phdr.p_flags & PF_W) != 0;
429 uint32_t file_page = phdr.p_offset & ~PGMASK;
430 uint32_t mem_page = phdr.p_vaddr & ~PGMASK;
431 uint32_t page_offset = phdr.p_vaddr & PGMASK;
432 uint32_t read_bytes, zero_bytes;
433 if (phdr.p_filesz > 0)
434 {
435 /* Normal segment.
436 Read initial part from disk and zero the rest. */
437 read_bytes = page_offset + phdr.p_filesz;
438 zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE)
439 - read_bytes);
440 }
441 else
442 {
443 /* Entirely zero.
444 Don't read anything from disk. */
445 read_bytes = 0;
446 zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE);
447 }
448 if (!load_segment (file, file_page, (void *) mem_page,
449 read_bytes, zero_bytes, writable))
450 goto done;
451 }
452 else
453 goto done;
454 break;
455 }
456 }
457
458 /* Set up stack. */
459 if (!setup_stack (esp))
460 goto done;
461
462 /* Start address. */
463 *eip = (void (*) (void)) ehdr.e_entry;
464
465 success = true;
466
467 done:
468 /* We arrive here whether the load is successful or not. */
469 if (success) {
470 process_current()->executable = file;
471 } else {
472 file_close (file);
473 }
474 lock_release (&filesys_lock);
475 return success;
476}
477
478/* load() helpers. */
479
480static bool install_page (void *upage, void *kpage, bool writable);
481
482/* Checks whether PHDR describes a valid, loadable segment in
483 FILE and returns true if so, false otherwise. */
484static bool
485validate_segment (const struct Elf32_Phdr *phdr, struct file *file)
486{
487 /* p_offset and p_vaddr must have the same page offset. */
488 if ((phdr->p_offset & PGMASK) != (phdr->p_vaddr & PGMASK))
489 return false;
490
491 /* p_offset must point within FILE. */
492 if (phdr->p_offset > (Elf32_Off) file_length (file))
493 return false;
494
495 /* p_memsz must be at least as big as p_filesz. */
496 if (phdr->p_memsz < phdr->p_filesz)
497 return false;
498
499 /* The segment must not be empty. */
500 if (phdr->p_memsz == 0)
501 return false;
502
503 /* The virtual memory region must both start and end within the
504 user address space range. */
505 if (!is_user_vaddr ((void *) phdr->p_vaddr))
506 return false;
507 if (!is_user_vaddr ((void *) (phdr->p_vaddr + phdr->p_memsz)))
508 return false;
509
510 /* The region cannot "wrap around" across the kernel virtual
511 address space. */
512 if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr)
513 return false;
514
515 /* Disallow mapping page 0.
516 Not only is it a bad idea to map page 0, but if we allowed
517 it then user code that passed a null pointer to system calls
518 could quite likely panic the kernel by way of null pointer
519 assertions in memcpy(), etc. */
520 if (phdr->p_vaddr < PGSIZE)
521 return false;
522
523 /* It's okay. */
524 return true;
525}
526
527/* Loads a segment starting at offset OFS in FILE at address
528 UPAGE. In total, READ_BYTES + ZERO_BYTES bytes of virtual
529 memory are initialized, as follows:
530
531 - READ_BYTES bytes at UPAGE must be read from FILE
532 starting at offset OFS.
533
534 - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed.
535
536 The pages initialized by this function must be writable by the
537 user process if WRITABLE is true, read-only otherwise.
538
539 Return true if successful, false if a memory allocation error
540 or disk read error occurs. */
541static bool
542load_segment (struct file *file, off_t ofs, uint8_t *upage,
543 uint32_t read_bytes, uint32_t zero_bytes, bool writable)
544{
545 ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
546 ASSERT (pg_ofs (upage) == 0);
547 ASSERT (ofs % PGSIZE == 0);
548
549 file_seek (file, ofs);
550 while (read_bytes > 0 || zero_bytes > 0)
551 {
552 /* Calculate how to fill this page.
553 We will read PAGE_READ_BYTES bytes from FILE
554 and zero the final PAGE_ZERO_BYTES bytes. */
555 size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
556 size_t page_zero_bytes = PGSIZE - page_read_bytes;
557
558 /* Get a page of memory. */
559 uint8_t *kpage = palloc_get_page (PAL_USER);
560 if (kpage == NULL)
561 return false;
562
563 /* Load this page. */
564 if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes)
565 {
566 palloc_free_page (kpage);
567 return false;
568 }
569 memset (kpage + page_read_bytes, 0, page_zero_bytes);
570
571 /* Add the page to the process's address space. */
572 if (!install_page (upage, kpage, writable))
573 {
574 palloc_free_page (kpage);
575 return false;
576 }
577
578 /* Advance. */
579 read_bytes -= page_read_bytes;
580 zero_bytes -= page_zero_bytes;
581 upage += PGSIZE;
582 }
583 return true;
584}
585
586/* Create a minimal stack by mapping a zeroed page at the top of
587 user virtual memory.
588 You will implement this function in the Project 0.
589 Consider using `hex_dump` for debugging purposes */
590static bool
591setup_stack (void **esp)
592{
593 uint8_t *kpage = NULL;
594
595 kpage = palloc_get_page (PAL_USER | PAL_ZERO);
596 if (kpage == NULL)
597 return false;
598
599 if (! install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true)) {
600 palloc_free_page (kpage);
601 return false;
602 }
603
604 /* Currently we assume that 'argc = 0' */
605 *esp = PHYS_BASE - 12;
606
607 return true;
608}
609
610/* Adds a mapping from user virtual address UPAGE to kernel
611 virtual address KPAGE to the page table.
612 If WRITABLE is true, the user process may modify the page;
613 otherwise, it is read-only.
614 UPAGE must not already be mapped.
615 KPAGE should probably be a page obtained from the user pool
616 with palloc_get_page().
617 Returns true on success, false if UPAGE is already mapped or
618 if memory allocation fails. */
619static bool
620install_page (void *upage, void *kpage, bool writable)
621{
622 struct thread *t = thread_current ();
623
624 /* Verify that there's not already a page at that virtual
625 address, then map our page there. */
626 return (pagedir_get_page (t->pagedir, upage) == NULL
627 && pagedir_set_page (t->pagedir, upage, kpage, writable));
628}
629
630static
631bool
632init_fd_table (struct fd_table *table)
633{
634 table->fds = palloc_get_page (PAL_ZERO);
635 if (table->fds == NULL)
636 return false;
637 table->fd_cap = PGSIZE / sizeof (table->fds[0]);
638 table->fd_free = 2;
639 table->fd_max = 1;
640 return true;
641}
642
643/* Open the file with the given name; returns
644 a file descriptor for this file if successful,
645 and a negative value otherwise */
646int
647process_open_file (const char* fname)
648{
649 struct fd_table *fdt = &process_current()->fd_table;
650 if (fdt->fd_free >= fdt->fd_cap)
651 return -1;
652
653 lock_acquire (&filesys_lock);
654 struct file *f = filesys_open (fname);
655 lock_release (&filesys_lock);
656
657 if (f == NULL)
658 return -1;
659
660 int fd = fdt->fd_free++;
661 fdt->fds[fd] = f;
662
663 /* update index of free/max file descriptor index*/
664 if (fd > fdt->fd_max) fdt->fd_max = fd;
665 while (fdt->fds[fdt->fd_free] != NULL) {
666 fdt->fd_free++;
667 if (fdt->fd_free >= fdt->fd_cap)
668 break;
669 }
670 return fd;
671}
672
673/* Get the file associated with the given file
674 descriptor; return NULL if no file is associated
675 with the given descriptor */
676struct file*
677process_get_file (int fd)
678{
679 struct fd_table *fdt = &process_current()->fd_table;
680 if (fd < 2 || fd >= fdt->fd_cap || ! fdt->fds[fd])
681 return NULL;
682 return fdt->fds[fd];
683}
684
685/* Acquire global lock for the filesystem */
686void process_lock_filesys (void)
687{
688 lock_acquire (&filesys_lock);
689}
690
691/* Release global filesystem lock */
692void process_unlock_filesys (void)
693{
694 lock_release (&filesys_lock);
695}
696
697/* Close the file associated with the given file
698 descriptor; returns true if close was successful */
699bool
700process_close_file (int fd)
701{
702 struct file *file = process_get_file (fd);
703 if (file == NULL)
704 return false;
705
706 lock_acquire (&filesys_lock);
707 file_close (file);
708 lock_release (&filesys_lock);
709
710 struct fd_table *fdt = &process_current()->fd_table;
711 fdt->fds[fd] = NULL;
712
713 /* update index of free/max file descriptor index*/
714 if (fd < fdt->fd_free) fdt->fd_free = fd;
715 while (fdt->fds[fdt->fd_max] == NULL) {
716 fdt->fd_max--;
717 if (fdt->fd_max < 2)
718 break;
719 }
720 return true;
721}
diff --git a/pintos-progos/userprog/process.h b/pintos-progos/userprog/process.h
new file mode 100644
index 0000000..1609801
--- /dev/null
+++ b/pintos-progos/userprog/process.h
@@ -0,0 +1,47 @@
1#ifndef USERPROG_PROCESS_H
2#define USERPROG_PROCESS_H
3
4#include "threads/thread.h"
5
6/* In the current implementation, the capacity is fixed to 1024 (PGSIZE/4) */
7struct fd_table {
8 struct file** fds;
9 int fd_free; /* lowest-index free FD table entry */
10 int fd_max; /* highest-index used FD table entry */
11 int fd_cap; /* FD table capacity */
12};
13
14struct process {
15 /* process tree */
16 tid_t thread_id;
17 tid_t parent_tid;
18 struct list_elem parentelem; /* Owned by parent */
19
20 /* communication with parent process */
21 struct semaphore exit_sem;
22 struct lock exit_lock;
23 int exit_status;
24
25 /* files */
26 struct file *executable; /* Loaded executable, if any. */
27 struct fd_table fd_table; /* File descriptor table */
28
29 /* Owned by syscall.c */
30 void* syscall_buffer;
31 size_t syscall_buffer_page_cnt;
32};
33
34void process_init (void);
35struct process* process_current (void);
36tid_t process_execute (const char *file_name);
37int process_wait (tid_t);
38void process_exit (void);
39void process_activate (void);
40
41int process_open_file(const char* fname);
42struct file* process_get_file(int fd);
43void process_lock_filesys (void);
44void process_unlock_filesys (void);
45bool process_close_file(int fd);
46
47#endif /* userprog/process.h */
diff --git a/pintos-progos/userprog/syscall.c b/pintos-progos/userprog/syscall.c
new file mode 100644
index 0000000..f8e0197
--- /dev/null
+++ b/pintos-progos/userprog/syscall.c
@@ -0,0 +1,563 @@
1#include <stdio.h>
2#include <syscall-nr.h>
3#include "devices/input.h"
4#include "devices/shutdown.h"
5#include "filesys/file.h"
6#include "filesys/filesys.h"
7#include "filesys/inode.h"
8#include "lib/string.h"
9#include "threads/interrupt.h"
10#include "threads/palloc.h"
11#include "threads/synch.h"
12#include "threads/thread.h"
13#include "threads/vaddr.h"
14#include "userprog/pagedir.h"
15#include "userprog/process.h"
16#include "userprog/syscall.h"
17
18#define STACK_SLOT_SIZE sizeof(int)
19
20/* Prototypes for Utilities */
21static int get_user (const uint8_t *uaddr);
22static bool put_user (uint8_t *udst, uint8_t byte);
23static void* memcpy_from_user (void *kaddr, void *uaddr, size_t bytes);
24static void* memcpy_to_user (void *kaddr, void *addr, size_t bytes);
25static void* copy_string_arg (void *usp, bool *segfault);
26static void free_string_arg_buf (void *kbuf);
27
28/* Reads a byte at user virtual address UADDR.
29 UADDR must be below PHYS_BASE.
30 Returns the byte value if successful, -1 if a segfault
31 occurred. */
32static int
33get_user (const uint8_t *uaddr)
34{
35 int result;
36 asm ("movl $1f, %0; " /* save eip in eax */
37 "movzbl %1, %0; " /* read byte from user memory into eax */
38 "1:" /* continue here on page fault, with eax set to -1 */
39 : "=&a" (result) : "m" (*uaddr));
40 return result;
41}
42
43/* Writes BYTE to user address UDST.
44 UDST must be below PHYS_BASE.
45 Returns true if successful, false if a segfault occurred. */
46static bool
47put_user (uint8_t *udst, uint8_t byte)
48{
49 int error_code;
50 asm ("movl $1f, %0;" /* save EIP in EAX */
51 "movb %b2, %1;" /* write byte to user memory */
52 "1:" /* continue here on page fault, with eax set to -1 */
53 : "=&a" (error_code), "=m" (*udst) : "q" (byte));
54 return error_code != -1;
55}
56
57/* Copy bytes from user space; returns NULL if a segfault
58 occured, and kaddr otherwise */
59static void*
60memcpy_from_user (void *kaddr, void *uaddr, size_t bytes)
61{
62 uint8_t* kp = kaddr;
63 size_t i;
64 if (! is_user_vaddr (uaddr) ||
65 ! is_user_vaddr (uaddr + bytes - 1))
66 return false;
67 for (i=0;i<bytes;i++) {
68 int b = get_user (uaddr+i);
69 if (b < 0)
70 break; /* segfault */
71 else
72 kp[i] = b;
73 }
74 if (i != bytes)
75 return NULL; /* segfault */
76 return kaddr;
77}
78
79/* Copy bytes to user space; returns NULL if a segfault
80 occured, and uaddr otherwise */
81static void*
82memcpy_to_user (void *uaddr, void *kaddr, size_t bytes)
83{
84 uint8_t* kp = kaddr;
85 size_t i;
86 if (! is_user_vaddr (uaddr) ||
87 ! is_user_vaddr (uaddr + bytes - 1))
88 return false;
89 for (i=0;i<bytes;i++) {
90 if (! put_user (uaddr+i, kp[i]))
91 break; /* segfault */
92 }
93 if (i != bytes)
94 return NULL; /* segfault */
95 return uaddr;
96}
97
98/* Copy string (at most cnt bytes) from user memory to the kernel address
99 `kaddr`. The number of bytes copied is returned; 0 indicates a segfault. */
100static size_t
101strncpy_from_user (void *kaddr, void *uaddr, size_t cnt)
102{
103 uint8_t *kp = (uint8_t*) kaddr;
104 size_t i;
105 int c = 1;
106 for (i = 0; i < cnt && c; i++) {
107 if (! is_user_vaddr (uaddr+i))
108 return 0; /* sefault */
109 c = get_user (uaddr+i);
110 if (c < 0)
111 return 0; /* segfault */
112 kp[i] = c;
113 }
114 return i;
115}
116
117/* Copy a value of scalar type `typeof(*kdst)` from user space pointer
118 `usrc` to kernel space variable pointed to by `kdst`. Returns
119 false on segfault, and true otherwise. */
120#define copy_from_user(kdst, usrc) \
121 (memcpy_from_user (kdst, usrc, sizeof (*kdst)) != NULL)
122
123/* Takes user space stack pointer, which points to a string
124 in user space. Copies that string (at most PGSIZE bytes)
125 to a freshly allocated buffer in kernel space. Reurns NULL on
126 error; on a segfault, additionally sets `segfault` to true */
127static void*
128copy_string_arg (void *usp, bool *segfault)
129{
130 size_t bytes_copied;
131 char *uptr;
132 char *kpage;
133 if (! copy_from_user (&uptr, usp)) {
134 *segfault = true;
135 return NULL;
136 }
137 kpage = palloc_get_page (PAL_ZERO);
138 if (kpage == NULL)
139 return NULL;
140 bytes_copied = strncpy_from_user (kpage, uptr, PGSIZE);
141 if (bytes_copied == 0) {
142 *segfault = true;
143 palloc_free_page (kpage);
144 return NULL;
145 }
146 if (kpage[bytes_copied-1] != '\0') {
147 palloc_free_page (kpage);
148 return NULL;
149 };
150 return kpage;
151}
152
153/* free buffer allocated by `copy_string_arg` */
154static void free_string_arg_buf (void *kbuf)
155{
156 palloc_free_page (kbuf);
157}
158
159/* Stack slot address */
160#define STACK_ADDR(sp, slot) (sp + STACK_SLOT_SIZE*slot)
161
162/* syscall handler prototype */
163static void syscall_handler (struct intr_frame *);
164
165typedef int (handler)(void *sp, bool *segfault);
166
167/* Prototypes for syscall handlers */
168static handler
169 syscall_halt,
170 syscall_exit,
171 syscall_write,
172 syscall_wait ,
173 syscall_exec,
174 syscall_create,
175 syscall_remove,
176 syscall_open,
177 syscall_filesize,
178 syscall_read,
179 syscall_seek,
180 syscall_tell,
181 syscall_close;
182
183/* Register syscall_handler for interrupt 0x30 */
184void
185syscall_init (void)
186{
187 intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
188}
189
190/* Syscall handler, delegating known syscalls to the respective implementations */
191static void
192syscall_handler (struct intr_frame *f)
193{
194 int syscall_nr;
195 handler* fp;
196 bool segfault = false;
197 int result;
198 void *sp = f->esp;
199
200 /* The system call number and the arguments are on the stack */
201 if (! copy_from_user (&syscall_nr,sp))
202 goto fail;
203 switch (syscall_nr) {
204 case SYS_HALT: fp = syscall_halt; break;
205 case SYS_EXIT: fp = syscall_exit; break;
206 case SYS_EXEC: fp = syscall_exec; break;
207 case SYS_WAIT: fp = syscall_wait; break;
208 case SYS_CREATE: fp = syscall_create; break;
209 case SYS_REMOVE: fp = syscall_remove; break;
210 case SYS_OPEN: fp = syscall_open; break;
211 case SYS_FILESIZE: fp = syscall_filesize; break;
212 case SYS_READ: fp = syscall_read; break;
213 case SYS_WRITE: fp = syscall_write; break;
214 case SYS_SEEK: fp = syscall_seek; break;
215 case SYS_TELL: fp = syscall_tell; break;
216 case SYS_CLOSE: fp = syscall_close; break;
217 default:
218 goto fail;
219 }
220 result = fp (sp, &segfault);
221 if (segfault)
222 goto fail;
223 f->eax = result;
224 return;
225
226 fail:
227 process_current()->exit_status = -1;
228 thread_exit ();
229}
230
231/* Shutdown machine */
232static int
233syscall_halt (void *sp UNUSED, bool *segfault UNUSED)
234{
235 shutdown ();
236 NOT_REACHED ();
237}
238
239/* Exit current process with given exit code */
240static int
241syscall_exit (void *sp, bool *segfault)
242{
243 int exit_status;
244 if (! copy_from_user (&exit_status, STACK_ADDR (sp,1))) {
245 *segfault = true;
246 return -1;
247 }
248 process_current()->exit_status = exit_status;
249 thread_exit ();
250 NOT_REACHED ();
251}
252
253/* Spawn new process executing the supplied command */
254static int
255syscall_exec (void *sp, bool *segfault)
256{
257 char *kbuf;
258 int result = TID_ERROR;
259 if ((kbuf = copy_string_arg (STACK_ADDR (sp, 1), segfault)) != NULL) {
260 result = process_execute (kbuf);
261 free_string_arg_buf (kbuf);
262 }
263 return result;
264}
265
266/* Wait until specified process exits */
267static int
268syscall_wait (void *sp, bool *segfault)
269{
270 tid_t arg;
271 if (! copy_from_user (&arg, STACK_ADDR (sp,1))) {
272 *segfault = true;
273 return 0;
274 }
275 return process_wait (arg);
276}
277
278/* Create a new file with given initial size */
279static int
280syscall_create (void *sp, bool *segfault)
281{
282 bool success = false;
283 char *fname;
284 int initial_size;
285
286 if (! copy_from_user (&initial_size, STACK_ADDR (sp,2))) {
287 *segfault = true;
288 return false;
289 }
290 if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL)
291 return false;
292
293 process_lock_filesys ();
294 success = filesys_create (fname, initial_size);
295 process_unlock_filesys ();
296 free_string_arg_buf (fname);
297 return success;
298}
299
300/* Remove name file, returns true if successful */
301static int
302syscall_remove (void *sp, bool *segfault)
303{
304 bool success;
305 char *fname;
306
307 if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL)
308 return false;
309 process_lock_filesys ();
310 success = filesys_remove (fname);
311 process_unlock_filesys ();
312 free_string_arg_buf (fname);
313 return (int)success;
314}
315
316/* Open file, returning non-negative file descriptor if successful */
317static int
318syscall_open (void *sp, bool *segfault)
319{
320 char *fname;
321 int fd;
322 if ((fname = copy_string_arg (STACK_ADDR (sp, 1), segfault)) == NULL)
323 return false;
324 fd = process_open_file (fname);
325 free_string_arg_buf (fname);
326 return fd;
327}
328
329/* Return size of file described by file descriptor */
330static int
331syscall_filesize (void *sp, bool *segfault)
332{
333 int fd;
334 struct file *f;
335 int size;
336
337 if (! copy_from_user (&fd, STACK_ADDR (sp,1))) {
338 *segfault = true;
339 return -1;
340 }
341 if ((f = process_get_file (fd)) == NULL)
342 return -1;
343 process_lock_filesys ();
344 size = inode_length (file_get_inode (f));
345 process_unlock_filesys ();
346 return size;
347}
348
349/* Read bytes from the file referenced by the given file
350 descriptor into the supplied user space buffer, returning
351 number of bytes read. */
352static int
353syscall_read (void *sp, bool *segfault)
354{
355 int fd;
356 uint8_t *user_buffer;
357 size_t size, bytes_to_read;
358
359 /* get arguments */
360 if (! copy_from_user (&fd, STACK_ADDR (sp,1)) ||
361 ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) ||
362 ! copy_from_user (&size, STACK_ADDR (sp,3))) {
363 *segfault = true;
364 return -1;
365 }
366
367 /* ensure buffer is in user space */
368 if (! is_user_vaddr (user_buffer) ||
369 ! is_user_vaddr (user_buffer + size - 1)) {
370 *segfault = true;
371 return -1;
372 }
373
374 bytes_to_read = size;
375 /* handle stdin */
376 if (fd == STDIN_FILENO) {
377 char c;
378 while (bytes_to_read--) {
379 c = input_getc ();
380 if (! put_user (user_buffer++, c)) {
381 *segfault = true;
382 return -1;
383 }
384 }
385 return size;
386 }
387 /* get file */
388 struct file *file = process_get_file (fd);
389 if (file == NULL)
390 return -1;
391
392 char *kbuf = palloc_get_page (0);
393 if (kbuf == NULL)
394 return -1;
395
396 /* read loop */
397 do {
398 int bytes_read;
399 int blocksize = bytes_to_read;
400 if (bytes_to_read > PGSIZE)
401 blocksize = PGSIZE;
402
403 /* read bytes */
404 process_lock_filesys ();
405 bytes_read = file_read (file, kbuf, blocksize);
406 process_unlock_filesys ();
407
408 /* Stop when EOF has been reached */
409 if (bytes_read == 0)
410 break;
411 bytes_to_read -= bytes_read;
412 if (! memcpy_to_user (user_buffer, kbuf, bytes_read)) {
413 *segfault = true;
414 break;
415 }
416 user_buffer += bytes_read;
417 } while (bytes_to_read > 0);
418
419 palloc_free_page (kbuf);
420 return size - bytes_to_read;
421}
422
423/* Write bytes from user buffer into the specified
424 file, returning number of bytes written. */
425static int
426syscall_write (void *sp, bool *segfault)
427{
428 int fd;
429 size_t size, bytes_to_write;
430 char *user_buffer;
431
432 /* get arguments */
433 if (! copy_from_user (&fd, STACK_ADDR (sp,1)) ||
434 ! copy_from_user (&user_buffer, STACK_ADDR (sp, 2)) ||
435 ! copy_from_user (&size, STACK_ADDR (sp,3))) {
436 *segfault = true;
437 return -1;
438 }
439
440 /* ensure buffer is in user space */
441 if (! is_user_vaddr (user_buffer) ||
442 ! is_user_vaddr (user_buffer + size - 1)) {
443 *segfault = true;
444 return -1;
445 }
446
447 /* get file handle */
448 struct file *file = NULL;
449 if (fd != STDOUT_FILENO) {
450 file = process_get_file (fd);
451 if (file == NULL)
452 return -1;
453 }
454
455 /* allocate kernel buffer */
456 char *kbuf = palloc_get_page (0);
457 if (kbuf == NULL)
458 return -1;
459
460 /* write loop */
461 bytes_to_write = size;
462 do {
463 int blocksize = bytes_to_write;
464 if (bytes_to_write > PGSIZE)
465 blocksize = PGSIZE;
466 if (memcpy_from_user (kbuf, user_buffer, blocksize) == NULL) {
467 *segfault = true;
468 break;
469 }
470 if (fd == STDOUT_FILENO) {
471 putbuf (kbuf, blocksize);
472 bytes_to_write -= blocksize;
473 } else {
474 int bytes_written = 0;
475 int bytes_left_filesys = blocksize;
476
477 process_lock_filesys ();
478 while (bytes_left_filesys > 0) {
479 bytes_written = file_write (file, kbuf, bytes_left_filesys);
480 if (bytes_written <= 0) {
481 break;
482 }
483 bytes_left_filesys -= bytes_written;
484 }
485 process_unlock_filesys ();
486
487 if (bytes_written <= 0)
488 break;
489 bytes_to_write -= blocksize;
490 }
491 user_buffer += blocksize;
492 } while (bytes_to_write > 0);
493
494 /* return bytes written */
495 palloc_free_page (kbuf);
496 return size - bytes_to_write;
497}
498
499/* Change the position where the next byte will be read or written */
500static int
501syscall_seek (void *sp, bool *segfault)
502{
503 int fd;
504 off_t new_pos;
505
506 /* get arguments */
507 if (! copy_from_user (&fd, STACK_ADDR (sp,1)) ||
508 ! copy_from_user (&new_pos, STACK_ADDR (sp, 2))) {
509 *segfault = true;
510 return 0;
511 }
512
513 /* no way to return something sensible (void function) */
514 struct file *file = process_get_file (fd);
515 if (file == NULL)
516 return 0;
517
518 process_lock_filesys ();
519 file_seek (file, new_pos);
520 process_unlock_filesys ();
521 return 0;
522}
523
524/* Returns the position of the next byte to be read or written */
525static int
526syscall_tell (void *sp, bool *segfault)
527{
528 int fd;
529 unsigned r = 0;
530
531 /* get arguments */
532 if (! copy_from_user (&fd, STACK_ADDR (sp,1))) {
533 *segfault = true;
534 return 0;
535 }
536
537 /* no way to return something sensible function */
538 struct file *file = process_get_file (fd);
539 if (file == NULL)
540 return 0;
541
542 process_lock_filesys ();
543 r = file_tell (file);
544 process_unlock_filesys ();
545 return r;
546}
547
548/* Close the given file */
549static int
550syscall_close (void *sp, bool *segfault)
551{
552 int fd;
553
554 /* get arguments */
555 if (! copy_from_user (&fd, STACK_ADDR (sp,1))) {
556 *segfault = true;
557 return 0;
558 }
559
560 /* no way to return something sensible function (void) */
561 (void) process_close_file (fd);
562 return 0;
563}
diff --git a/pintos-progos/userprog/syscall.h b/pintos-progos/userprog/syscall.h
new file mode 100644
index 0000000..f7ab2f3
--- /dev/null
+++ b/pintos-progos/userprog/syscall.h
@@ -0,0 +1,5 @@
1#ifndef USERPROG_SYSCALL_H
2#define USERPROG_SYSCALL_H
3
4void syscall_init (void);
5#endif /* userprog/syscall.h */
diff --git a/pintos-progos/userprog/tss.c b/pintos-progos/userprog/tss.c
new file mode 100644
index 0000000..f8ed9a9
--- /dev/null
+++ b/pintos-progos/userprog/tss.c
@@ -0,0 +1,106 @@
1#include "userprog/tss.h"
2#include <debug.h>
3#include <stddef.h>
4#include "userprog/gdt.h"
5#include "threads/thread.h"
6#include "threads/palloc.h"
7#include "threads/vaddr.h"
8
9/* The Task-State Segment (TSS).
10
11 Instances of the TSS, an x86-specific structure, are used to
12 define "tasks", a form of support for multitasking built right
13 into the processor. However, for various reasons including
14 portability, speed, and flexibility, most x86 OSes almost
15 completely ignore the TSS. We are no exception.
16
17 Unfortunately, there is one thing that can only be done using
18 a TSS: stack switching for interrupts that occur in user mode.
19 When an interrupt occurs in user mode (ring 3), the processor
20 consults the ss0 and esp0 members of the current TSS to
21 determine the stack to use for handling the interrupt. Thus,
22 we must create a TSS and initialize at least these fields, and
23 this is precisely what this file does.
24
25 When an interrupt is handled by an interrupt or trap gate
26 (which applies to all interrupts we handle), an x86 processor
27 works like this:
28
29 - If the code interrupted by the interrupt is in the same
30 ring as the interrupt handler, then no stack switch takes
31 place. This is the case for interrupts that happen when
32 we're running in the kernel. The contents of the TSS are
33 irrelevant for this case.
34
35 - If the interrupted code is in a different ring from the
36 handler, then the processor switches to the stack
37 specified in the TSS for the new ring. This is the case
38 for interrupts that happen when we're in user space. It's
39 important that we switch to a stack that's not already in
40 use, to avoid corruption. Because we're running in user
41 space, we know that the current process's kernel stack is
42 not in use, so we can always use that. Thus, when the
43 scheduler switches threads, it also changes the TSS's
44 stack pointer to point to the new thread's kernel stack.
45 (The call is in thread_schedule_tail() in thread.c.)
46
47 See [IA32-v3a] 6.2.1 "Task-State Segment (TSS)" for a
48 description of the TSS. See [IA32-v3a] 5.12.1 "Exception- or
49 Interrupt-Handler Procedures" for a description of when and
50 how stack switching occurs during an interrupt. */
51struct tss
52 {
53 uint16_t back_link, :16;
54 void *esp0; /* Ring 0 stack virtual address. */
55 uint16_t ss0, :16; /* Ring 0 stack segment selector. */
56 void *esp1;
57 uint16_t ss1, :16;
58 void *esp2;
59 uint16_t ss2, :16;
60 uint32_t cr3;
61 void (*eip) (void);
62 uint32_t eflags;
63 uint32_t eax, ecx, edx, ebx;
64 uint32_t esp, ebp, esi, edi;
65 uint16_t es, :16;
66 uint16_t cs, :16;
67 uint16_t ss, :16;
68 uint16_t ds, :16;
69 uint16_t fs, :16;
70 uint16_t gs, :16;
71 uint16_t ldt, :16;
72 uint16_t trace, bitmap;
73 };
74
75/* Kernel TSS. */
76static struct tss *tss;
77
78/* Initializes the kernel TSS. */
79void
80tss_init (void)
81{
82 /* Our TSS is never used in a call gate or task gate, so only a
83 few fields of it are ever referenced, and those are the only
84 ones we initialize. */
85 tss = palloc_get_page (PAL_ASSERT | PAL_ZERO);
86 tss->ss0 = SEL_KDSEG;
87 tss->bitmap = 0xdfff;
88 tss_update ();
89}
90
91/* Returns the kernel TSS. */
92struct tss *
93tss_get (void)
94{
95 ASSERT (tss != NULL);
96 return tss;
97}
98
99/* Sets the ring 0 stack pointer in the TSS to point to the end
100 of the thread stack. */
101void
102tss_update (void)
103{
104 ASSERT (tss != NULL);
105 tss->esp0 = (uint8_t *) thread_current () + PGSIZE;
106}
diff --git a/pintos-progos/userprog/tss.h b/pintos-progos/userprog/tss.h
new file mode 100644
index 0000000..467bd19
--- /dev/null
+++ b/pintos-progos/userprog/tss.h
@@ -0,0 +1,11 @@
1#ifndef USERPROG_TSS_H
2#define USERPROG_TSS_H
3
4#include <stdint.h>
5
6struct tss;
7void tss_init (void);
8struct tss *tss_get (void);
9void tss_update (void);
10
11#endif /* userprog/tss.h */
diff --git a/pintos-progos/utils/.gitignore b/pintos-progos/utils/.gitignore
new file mode 100644
index 0000000..b96f278
--- /dev/null
+++ b/pintos-progos/utils/.gitignore
@@ -0,0 +1,3 @@
1setitimer-helper
2squish-pty
3squish-unix
diff --git a/pintos-progos/utils/Makefile b/pintos-progos/utils/Makefile
new file mode 100644
index 0000000..46a9124
--- /dev/null
+++ b/pintos-progos/utils/Makefile
@@ -0,0 +1,11 @@
1all: setitimer-helper squish-pty squish-unix
2
3CC = gcc
4CFLAGS = -Wall -W
5LDFLAGS = -lm
6setitimer-helper: setitimer-helper.o
7squish-pty: squish-pty.o
8squish-unix: squish-unix.o
9
10clean:
11 rm -f *.o setitimer-helper squish-pty squish-unix
diff --git a/pintos-progos/utils/Pintos.pm b/pintos-progos/utils/Pintos.pm
new file mode 100644
index 0000000..70df40d
--- /dev/null
+++ b/pintos-progos/utils/Pintos.pm
@@ -0,0 +1,491 @@
1# Pintos helper subroutines.
2
3# Number of bytes available for the loader at the beginning of the MBR.
4# Kernel command-line arguments follow the loader.
5our $LOADER_SIZE = 314;
6
7# Partition types.
8my (%role2type) = (KERNEL => 0x20,
9 FILESYS => 0x21,
10 SCRATCH => 0x22,
11 SWAP => 0x23);
12my (%type2role) = reverse %role2type;
13
14# Order of roles within a given disk.
15our (@role_order) = qw (KERNEL FILESYS SCRATCH SWAP);
16
17# Partitions.
18#
19# Valid keys are KERNEL, FILESYS, SCRATCH, SWAP. Only those
20# partitions which are in use are included.
21#
22# Each value is a reference to a hash. If the partition's contents
23# are to be obtained from a file (that will be copied into a new
24# virtual disk), then the hash contains:
25#
26# FILE => name of file from which the partition's contents are copied
27# (perhaps "/dev/zero"),
28# OFFSET => offset in bytes in FILE,
29# BYTES => size in bytes of contents from FILE,
30#
31# If the partition is taken from a virtual disk directly, then it
32# contains the following. The same keys are also filled in once a
33# file-based partition has been copied into a new virtual disk:
34#
35# DISK => name of virtual disk file,
36# START => sector offset of start of partition within DISK,
37# SECTORS => number of sectors of partition within DISK, which is usually
38# greater than round_up (BYTES, 512) due to padding.
39our (%parts);
40
41# set_part($opt, $arg)
42#
43# For use as a helper function for Getopt::Long::GetOptions to set
44# disk sources.
45sub set_part {
46 my ($opt, $arg) = @_;
47 my ($role, $source) = $opt =~ /^([a-z]+)(?:-([a-z]+))?/ or die;
48
49 $role = uc $role;
50 $source = 'FILE' if $source eq '';
51
52 die "can't have two sources for \L$role\E partition"
53 if exists $parts{$role};
54
55 do_set_part ($role, $source, $arg);
56}
57
58# do_set_part($role, $source, $arg)
59#
60# Sets partition $role as coming from $source (one of 'file', 'from',
61# or 'size'). $arg is a file name for 'file' or 'from', a size in
62# megabytes for 'size'.
63sub do_set_part {
64 my ($role, $source, $arg) = @_;
65
66 my ($p) = $parts{$role} = {};
67 if ($source eq 'file') {
68 if (read_mbr ($arg)) {
69 print STDERR "warning: $arg looks like a partitioned disk ";
70 print STDERR "(did you want --$role-from=$arg or --disk=$arg?)\n"
71 }
72
73 $p->{FILE} = $arg;
74 $p->{OFFSET} = 0;
75 $p->{BYTES} = -s $arg;
76 } elsif ($source eq 'from') {
77 my (%pt) = read_partition_table ($arg);
78 my ($sp) = $pt{$role};
79 die "$arg: does not contain \L$role\E partition\n" if !defined $sp;
80
81 $p->{FILE} = $arg;
82 $p->{OFFSET} = $sp->{START} * 512;
83 $p->{BYTES} = $sp->{SECTORS} * 512;
84 } elsif ($source eq 'size') {
85 $arg =~ /^\d+(\.\d+)?|\.\d+$/ or die "$arg: not a valid size in MB\n";
86
87 $p->{FILE} = "/dev/zero";
88 $p->{OFFSET} = 0;
89 $p->{BYTES} = ceil ($arg * 1024 * 1024);
90 } else {
91 die;
92 }
93}
94
95# set_geometry('HEADS,SPT')
96# set_geometry('zip')
97#
98# For use as a helper function for Getopt::Long::GetOptions to set
99# disk geometry.
100sub set_geometry {
101 local ($_) = $_[1];
102 if ($_ eq 'zip') {
103 @geometry{'H', 'S'} = (64, 32);
104 } else {
105 @geometry{'H', 'S'} = /^(\d+)[,\s]+(\d+)$/
106 or die "bad syntax for geometry\n";
107 $geometry{H} <= 255 or die "heads limited to 255\n";
108 $geometry{S} <= 63 or die "sectors per track limited to 63\n";
109 }
110}
111
112# set_align('bochs|full|none')
113#
114# For use as a helper function for Getopt::Long::GetOptions to set
115# partition alignment.
116sub set_align {
117 $align = $_[1];
118 die "unknown alignment type \"$align\"\n"
119 if $align ne 'bochs' && $align ne 'full' && $align ne 'none';
120}
121
122# assemble_disk(%args)
123#
124# Creates a virtual disk $args{DISK} containing the partitions
125# described by @args{KERNEL, FILESYS, SCRATCH, SWAP}.
126#
127# Required arguments:
128# DISK => output disk file name
129# HANDLE => output file handle (will be closed)
130#
131# Normally at least one of the following is included:
132# KERNEL, FILESYS, SCRATCH, SWAP => {input:
133# FILE => file to read,
134# OFFSET => byte offset in file,
135# BYTES => byte count from file,
136#
137# output:
138# DISK => output disk file name,
139# START => sector offset in DISK,
140# SECTORS => sector count in DISK},
141#
142# Optional arguments:
143# ALIGN => 'bochs' (default), 'full', or 'none'
144# GEOMETRY => {H => heads, S => sectors per track} (default 16, 63)
145# FORMAT => 'partitioned' (default) or 'raw'
146# LOADER => $LOADER_SIZE-byte string containing the loader binary
147# ARGS => ['arg 1', 'arg 2', ...]
148sub assemble_disk {
149 my (%args) = @_;
150
151 my (%geometry) = $args{GEOMETRY} || (H => 16, S => 63);
152
153 my ($align); # Align partition start, end to cylinder boundary?
154 my ($pad); # Pad end of disk out to cylinder boundary?
155 if (!defined ($args{ALIGN}) || $args{ALIGN} eq 'bochs') {
156 $align = 0;
157 $pad = 1;
158 } elsif ($args{ALIGN} eq 'full') {
159 $align = 1;
160 $pad = 0;
161 } elsif ($args{ALIGN} eq 'none') {
162 $align = $pad = 0;
163 } else {
164 die;
165 }
166
167 my ($format) = $args{FORMAT} || 'partitioned';
168 die if $format ne 'partitioned' && $format ne 'raw';
169
170 # Check that we have apartitions to copy in.
171 my $part_cnt = grep (defined ($args{$_}), keys %role2type);
172 die "must have exactly one partition for raw output\n"
173 if $format eq 'raw' && $part_cnt != 1;
174
175 # Calculate the disk size.
176 my ($total_sectors) = 0;
177 if ($format eq 'partitioned') {
178 $total_sectors += $align ? $geometry{S} : 1;
179 }
180 for my $role (@role_order) {
181 my ($p) = $args{$role};
182 next if !defined $p;
183
184 die if $p->{DISK};
185
186 my ($bytes) = $p->{BYTES};
187 my ($start) = $total_sectors;
188 my ($end) = $start + div_round_up ($bytes, 512);
189 $end = round_up ($end, cyl_sectors (%geometry)) if $align;
190
191 $p->{DISK} = $args{DISK};
192 $p->{START} = $start;
193 $p->{SECTORS} = $end - $start;
194 $total_sectors = $end;
195 }
196
197 # Write the disk.
198 my ($disk_fn) = $args{DISK};
199 my ($disk) = $args{HANDLE};
200 if ($format eq 'partitioned') {
201 # Pack loader into MBR.
202 my ($loader) = $args{LOADER} || "\xcd\x18";
203 my ($mbr) = pack ("a$LOADER_SIZE", $loader);
204
205 $mbr .= make_kernel_command_line (@{$args{ARGS}});
206
207 # Pack partition table into MBR.
208 $mbr .= make_partition_table (\%geometry, \%args);
209
210 # Add signature to MBR.
211 $mbr .= pack ("v", 0xaa55);
212
213 die if length ($mbr) != 512;
214 write_fully ($disk, $disk_fn, $mbr);
215 write_zeros ($disk, $disk_fn, 512 * ($geometry{S} - 1)) if $align;
216 }
217 for my $role (@role_order) {
218 my ($p) = $args{$role};
219 next if !defined $p;
220
221 my ($source);
222 my ($fn) = $p->{FILE};
223 open ($source, '<', $fn) or die "$fn: open: $!\n";
224 if ($p->{OFFSET}) {
225 sysseek ($source, $p->{OFFSET}, 0) == $p->{OFFSET}
226 or die "$fn: seek: $!\n";
227 }
228 copy_file ($source, $fn, $disk, $disk_fn, $p->{BYTES});
229 close ($source) or die "$fn: close: $!\n";
230
231 write_zeros ($disk, $disk_fn, $p->{SECTORS} * 512 - $p->{BYTES});
232 }
233 if ($pad) {
234 my ($pad_sectors) = round_up ($total_sectors, cyl_sectors (%geometry));
235 write_zeros ($disk, $disk_fn, ($pad_sectors - $total_sectors) * 512);
236 }
237 close ($disk) or die "$disk: close: $!\n";
238}
239
240# make_partition_table({H => heads, S => sectors}, {KERNEL => ..., ...})
241#
242# Creates and returns a partition table for the given partitions and
243# disk geometry.
244sub make_partition_table {
245 my ($geometry, $partitions) = @_;
246 my ($table) = '';
247 for my $role (@role_order) {
248 defined (my $p = $partitions->{$role}) or next;
249
250 my $end = $p->{START} + $p->{SECTORS} - 1;
251 my $bootable = $role eq 'KERNEL';
252
253 $table .= pack ("C", $bootable ? 0x80 : 0); # Bootable?
254 $table .= pack_chs ($p->{START}, $geometry); # CHS of partition start
255 $table .= pack ("C", $role2type{$role}); # Partition type
256 $table .= pack_chs($end, $geometry); # CHS of partition end
257 $table .= pack ("V", $p->{START}); # LBA of partition start
258 $table .= pack ("V", $p->{SECTORS}); # Length in sectors
259 die if length ($table) % 16;
260 }
261 return pack ("a64", $table);
262}
263
264# make_kernel_command_line(@args)
265#
266# Returns the raw bytes to write to an MBR at offset $LOADER_SIZE to
267# set a Pintos kernel command line.
268sub make_kernel_command_line {
269 my (@args) = @_;
270 my ($args) = join ('', map ("$_\0", @args));
271 die "command line exceeds 128 bytes" if length ($args) > 128;
272 return pack ("V a128", scalar (@args), $args);
273}
274
275# copy_file($from_handle, $from_file_name, $to_handle, $to_file_name, $size)
276#
277# Copies $size bytes from $from_handle to $to_handle.
278# $from_file_name and $to_file_name are used in error messages.
279sub copy_file {
280 my ($from_handle, $from_file_name, $to_handle, $to_file_name, $size) = @_;
281
282 while ($size > 0) {
283 my ($chunk_size) = 4096;
284 $chunk_size = $size if $chunk_size > $size;
285 $size -= $chunk_size;
286
287 my ($data) = read_fully ($from_handle, $from_file_name, $chunk_size);
288 write_fully ($to_handle, $to_file_name, $data);
289 }
290}
291
292# read_fully($handle, $file_name, $bytes)
293#
294# Reads exactly $bytes bytes from $handle and returns the data read.
295# $file_name is used in error messages.
296sub read_fully {
297 my ($handle, $file_name, $bytes) = @_;
298 my ($data);
299 my ($read_bytes) = sysread ($handle, $data, $bytes);
300 die "$file_name: read: $!\n" if !defined $read_bytes;
301 die "$file_name: unexpected end of file\n" if $read_bytes != $bytes;
302 return $data;
303}
304
305# write_fully($handle, $file_name, $data)
306#
307# Write $data to $handle.
308# $file_name is used in error messages.
309sub write_fully {
310 my ($handle, $file_name, $data) = @_;
311 my ($written_bytes) = syswrite ($handle, $data);
312 die "$file_name: write: $!\n" if !defined $written_bytes;
313 die "$file_name: short write\n" if $written_bytes != length $data;
314}
315
316sub write_zeros {
317 my ($handle, $file_name, $size) = @_;
318
319 while ($size > 0) {
320 my ($chunk_size) = 4096;
321 $chunk_size = $size if $chunk_size > $size;
322 $size -= $chunk_size;
323
324 write_fully ($handle, $file_name, "\0" x $chunk_size);
325 }
326}
327
328# div_round_up($x,$y)
329#
330# Returns $x / $y, rounded up to the nearest integer.
331# $y must be an integer.
332sub div_round_up {
333 my ($x, $y) = @_;
334 return int ((ceil ($x) + $y - 1) / $y);
335}
336
337# round_up($x, $y)
338#
339# Returns $x rounded up to the nearest multiple of $y.
340# $y must be an integer.
341sub round_up {
342 my ($x, $y) = @_;
343 return div_round_up ($x, $y) * $y;
344}
345
346# cyl_sectors(H => heads, S => sectors)
347#
348# Returns the number of sectors in a cylinder of a disk with the given
349# geometry.
350sub cyl_sectors {
351 my (%geometry) = @_;
352 return $geometry{H} * $geometry{S};
353}
354
355# read_loader($file_name)
356#
357# Reads and returns the first $LOADER_SIZE bytes in $file_name.
358# If $file_name is undefined, tries to find the default loader.
359# Makes sure that the loader is a reasonable size.
360sub read_loader {
361 my ($name) = @_;
362 $name = find_file ("loader.bin") if !defined $name;
363 die "Cannot find loader\n" if !defined $name;
364
365 my ($handle);
366 open ($handle, '<', $name) or die "$name: open: $!\n";
367 -s $handle == $LOADER_SIZE || -s $handle == 512
368 or die "$name: must be exactly $LOADER_SIZE or 512 bytes long\n";
369 $loader = read_fully ($handle, $name, $LOADER_SIZE);
370 close ($handle) or die "$name: close: $!\n";
371 return $loader;
372}
373
374# pack_chs($lba, {H => heads, S => sectors})
375#
376# Converts logical sector $lba to a 3-byte packed geometrical sector
377# in the format used in PC partition tables (see [Partitions]) and
378# returns the geometrical sector as a 3-byte string.
379sub pack_chs {
380 my ($lba, $geometry) = @_;
381 my ($cyl, $head, $sect) = lba_to_chs ($lba, $geometry);
382 return pack ("CCC", $head, $sect | (($cyl >> 2) & 0xc0), $cyl & 0xff);
383}
384
385# lba_to_chs($lba, {H => heads, S => sectors})
386#
387# Returns the geometrical sector corresponding to logical sector $lba
388# given the specified geometry.
389sub lba_to_chs {
390 my ($lba, $geometry) = @_;
391 my ($hpc) = $geometry->{H};
392 my ($spt) = $geometry->{S};
393
394 # Source:
395 # http://en.wikipedia.org/wiki/CHS_conversion
396 use integer;
397 my $cyl = $lba / ($hpc * $spt);
398 my $temp = $lba % ($hpc * $spt);
399 my $head = $temp / $spt;
400 my $sect = $temp % $spt + 1;
401
402 # Source:
403 # http://www.cgsecurity.org/wiki/Intel_Partition_Table
404 if ($cyl <= 1023) {
405 return ($cyl, $head, $sect);
406 } else {
407 return (1023, 254, 63); ## or should this be (1023, $hpc, $spt)?
408 }
409}
410
411# read_mbr($file)
412#
413# Tries to read an MBR from $file. Returns the 512-byte MBR if
414# successful, otherwise numeric 0.
415sub read_mbr {
416 my ($file) = @_;
417 my ($retval) = 0;
418 open (FILE, '<', $file) or die "$file: open: $!\n";
419 if (-s FILE == 0) {
420 die "$file: file has zero size\n";
421 } elsif (-s FILE >= 512) {
422 my ($mbr);
423 sysread (FILE, $mbr, 512) == 512 or die "$file: read: $!\n";
424 $retval = $mbr if unpack ("v", substr ($mbr, 510)) == 0xaa55;
425 }
426 close (FILE);
427 return $retval;
428}
429
430# interpret_partition_table($mbr, $disk)
431#
432# Parses the partition-table in the specified 512-byte $mbr and
433# returns the partitions. $disk is used for error messages.
434sub interpret_partition_table {
435 my ($mbr, $disk) = @_;
436 my (%parts);
437 for my $i (0...3) {
438 my ($bootable, $valid, $type, $lba_start, $lba_length)
439 = unpack ("C X V C x3 V V", substr ($mbr, 446 + 16 * $i, 16));
440 next if !$valid;
441
442 (print STDERR "warning: invalid partition entry $i in $disk\n"),
443 next if $bootable != 0 && $bootable != 0x80;
444
445 my ($role) = $type2role{$type};
446 (printf STDERR "warning: non-Pintos partition type 0x%02x in %s\n",
447 $type, $disk),
448 next if !defined $role;
449
450 (print STDERR "warning: duplicate \L$role\E partition in $disk\n"),
451 next if exists $parts{$role};
452
453 $parts{$role} = {START => $lba_start,
454 SECTORS => $lba_length};
455 }
456 return %parts;
457}
458
459# find_file($base_name)
460#
461# Looks for a file named $base_name in a couple of likely spots. If
462# found, returns the name; otherwise, returns undef.
463sub find_file {
464 my ($base_name) = @_;
465 -e && return $_ foreach $base_name, "build/$base_name";
466 return undef;
467}
468
469# read_partition_table($file)
470#
471# Reads a partition table from $file and returns the parsed
472# partitions. Dies if partitions can't be read.
473sub read_partition_table {
474 my ($file) = @_;
475 my ($mbr) = read_mbr ($file);
476 die "$file: not a partitioned disk\n" if !$mbr;
477 return interpret_partition_table ($mbr, $file);
478}
479
480# max(@args)
481#
482# Returns the numerically largest value in @args.
483sub max {
484 my ($max) = $_[0];
485 foreach (@_[1..$#_]) {
486 $max = $_ if $_ > $max;
487 }
488 return $max;
489}
490
4911;
diff --git a/pintos-progos/utils/backtrace b/pintos-progos/utils/backtrace
new file mode 100755
index 0000000..95e422f
--- /dev/null
+++ b/pintos-progos/utils/backtrace
@@ -0,0 +1,106 @@
1#! /usr/bin/perl -w
2
3use strict;
4
5# Check command line.
6if (grep ($_ eq '-h' || $_ eq '--help', @ARGV)) {
7 print <<'EOF';
8backtrace, for converting raw addresses into symbolic backtraces
9usage: backtrace [BINARY]... ADDRESS...
10where BINARY is the binary file or files from which to obtain symbols
11 and ADDRESS is a raw address to convert to a symbol name.
12
13If no BINARY is unspecified, the default is the first of kernel.o or
14build/kernel.o that exists. If multiple binaries are specified, each
15symbol printed is from the first binary that contains a match.
16
17The ADDRESS list should be taken from the "Call stack:" printed by the
18kernel. Read "Backtraces" in the "Debugging Tools" chapter of the
19Pintos documentation for more information.
20EOF
21 exit 0;
22}
23die "backtrace: at least one argument required (use --help for help)\n"
24 if @ARGV == 0;
25
26# Drop garbage inserted by kernel.
27@ARGV = grep (!/^(call|stack:?|[-+])$/i, @ARGV);
28s/\.$// foreach @ARGV;
29
30# Find binaries.
31my (@binaries);
32while ($ARGV[0] !~ /^0x/) {
33 my ($bin) = shift @ARGV;
34 die "backtrace: $bin: not found (use --help for help)\n" if ! -e $bin;
35 push (@binaries, $bin);
36}
37if (!@binaries) {
38 my ($bin);
39 if (-e 'kernel.o') {
40 $bin = 'kernel.o';
41 } elsif (-e 'build/kernel.o') {
42 $bin = 'build/kernel.o';
43 } else {
44 die "backtrace: no binary specified and neither \"kernel.o\" nor \"build/kernel.o\" exists (use --help for help)\n";
45 }
46 push (@binaries, $bin);
47}
48
49# Find addr2line.
50my ($a2l) = search_path ("i386-elf-addr2line") || search_path ("addr2line");
51if (!$a2l) {
52 die "backtrace: neither `i386-elf-addr2line' nor `addr2line' in PATH\n";
53}
54sub search_path {
55 my ($target) = @_;
56 for my $dir (split (':', $ENV{PATH})) {
57 my ($file) = "$dir/$target";
58 return $file if -e $file;
59 }
60 return undef;
61}
62
63# Figure out backtrace.
64my (@locs) = map ({ADDR => $_}, @ARGV);
65for my $bin (@binaries) {
66 open (A2L, "$a2l -fe $bin " . join (' ', map ($_->{ADDR}, @locs)) . "|");
67 for (my ($i) = 0; <A2L>; $i++) {
68 my ($function, $line);
69 chomp ($function = $_);
70 chomp ($line = <A2L>);
71 next if defined $locs[$i]{BINARY};
72
73 if ($function ne '??' || $line ne '??:0') {
74 $locs[$i]{FUNCTION} = $function;
75 $locs[$i]{LINE} = $line;
76 $locs[$i]{BINARY} = $bin;
77 }
78 }
79 close (A2L);
80}
81
82# Print backtrace.
83my ($cur_binary);
84for my $loc (@locs) {
85 if (defined ($loc->{BINARY})
86 && @binaries > 1
87 && (!defined ($cur_binary) || $loc->{BINARY} ne $cur_binary)) {
88 $cur_binary = $loc->{BINARY};
89 print "In $cur_binary:\n";
90 }
91
92 my ($addr) = $loc->{ADDR};
93 $addr = sprintf ("0x%08x", hex ($addr)) if $addr =~ /^0x[0-9a-f]+$/i;
94
95 print $addr, ": ";
96 if (defined ($loc->{BINARY})) {
97 my ($function) = $loc->{FUNCTION};
98 my ($line) = $loc->{LINE};
99 $line =~ s/^(\.\.\/)*//;
100 $line = "..." . substr ($line, -25) if length ($line) > 28;
101 print "$function ($line)";
102 } else {
103 print "(unknown)";
104 }
105 print "\n";
106}
diff --git a/pintos-progos/utils/pintos b/pintos-progos/utils/pintos
new file mode 100755
index 0000000..91f73ad
--- /dev/null
+++ b/pintos-progos/utils/pintos
@@ -0,0 +1,955 @@
1#! /usr/bin/perl -w
2
3use strict;
4use POSIX;
5use Fcntl;
6use File::Temp 'tempfile';
7use Getopt::Long qw(:config bundling);
8use Fcntl qw(SEEK_SET SEEK_CUR);
9
10# Read Pintos.pm from the same directory as this program.
11BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; }
12
13# Command-line options.
14our ($start_time) = time ();
15our ($sim); # Simulator: bochs, qemu, or player.
16our ($debug) = "none"; # Debugger: none, monitor, or gdb.
17our ($mem) = 4; # Physical RAM in MB.
18our ($serial) = 1; # Use serial port for input and output?
19our ($vga); # VGA output: window, terminal, or none.
20our ($jitter); # Seed for random timer interrupts, if set.
21our ($realtime); # Synchronize timer interrupts with real time?
22our ($timeout); # Maximum runtime in seconds, if set.
23our ($kill_on_failure); # Abort quickly on test failure?
24our ($kernel_test); # Run kernel test instead of user program
25our (@puts); # Files to copy into the VM.
26our (@gets); # Files to copy out of the VM.
27our ($as_ref); # Reference to last addition to @gets or @puts.
28our (@kernel_args); # Arguments to pass to kernel.
29our (%parts); # Partitions.
30our ($make_disk); # Name of disk to create.
31our ($tmp_disk) = 1; # Delete $make_disk after run?
32our (@disks); # Extra disk images to pass to simulator.
33our ($loader_fn); # Bootstrap loader.
34our (%geometry); # IDE disk geometry.
35our ($align); # Partition alignment.
36
37parse_command_line ();
38prepare_scratch_disk ();
39find_disks ();
40run_vm ();
41finish_scratch_disk ();
42
43exit 0;
44
45# Parses the command line.
46sub parse_command_line {
47 usage (0) if @ARGV == 0 || (@ARGV == 1 && $ARGV[0] eq '--help');
48
49 @kernel_args = @ARGV;
50 if (grep ($_ eq '--', @kernel_args)) {
51 @ARGV = ();
52 while ((my $arg = shift (@kernel_args)) ne '--') {
53 push (@ARGV, $arg);
54 }
55 GetOptions ("sim=s" => sub { set_sim ($_[1]) },
56 "bochs" => sub { set_sim ("bochs") },
57 "qemu" => sub { set_sim ("qemu") },
58 "player" => sub { set_sim ("player") },
59
60 "debug=s" => sub { set_debug ($_[1]) },
61 "no-debug" => sub { set_debug ("none") },
62 "monitor" => sub { set_debug ("monitor") },
63 "gdb" => sub { set_debug ("gdb") },
64
65 "m|memory=i" => \$mem,
66 "j|jitter=i" => sub { set_jitter ($_[1]) },
67 "r|realtime" => sub { set_realtime () },
68
69 "T|timeout=i" => \$timeout,
70 "k|kill-on-failure" => \$kill_on_failure,
71
72 "v|no-vga" => sub { set_vga ('none'); },
73 "s|no-serial" => sub { $serial = 0; },
74 "t|terminal" => sub { set_vga ('terminal'); },
75
76 "kernel-test" => sub { set_kernel_test(); },
77 "p|put-file=s" => sub { add_file (\@puts, $_[1]); },
78 "g|get-file=s" => sub { add_file (\@gets, $_[1]); },
79 "a|as=s" => sub { set_as ($_[1]); },
80
81 "h|help" => sub { usage (0); },
82
83 "kernel=s" => \&set_part,
84 "filesys=s" => \&set_part,
85 "swap=s" => \&set_part,
86
87 "filesys-size=s" => \&set_part,
88 "scratch-size=s" => \&set_part,
89 "swap-size=s" => \&set_part,
90
91 "kernel-from=s" => \&set_part,
92 "filesys-from=s" => \&set_part,
93 "swap-from=s" => \&set_part,
94
95 "make-disk=s" => sub { $make_disk = $_[1];
96 $tmp_disk = 0; },
97 "disk=s" => sub { set_disk ($_[1]); },
98 "loader=s" => \$loader_fn,
99
100 "geometry=s" => \&set_geometry,
101 "align=s" => \&set_align)
102 or exit 1;
103 }
104
105 $sim = "bochs" if !defined $sim;
106 $debug = "none" if !defined $debug;
107 $vga = exists ($ENV{DISPLAY}) ? "window" : "none" if !defined $vga;
108
109 undef $timeout, print "warning: disabling timeout with --$debug\n"
110 if defined ($timeout) && $debug ne 'none';
111
112 print "warning: enabling serial port for -k or --kill-on-failure\n"
113 if $kill_on_failure && !$serial;
114
115 $align = "bochs",
116 print STDERR "warning: setting --align=bochs for Bochs support\n"
117 if $sim eq 'bochs' && defined ($align) && $align eq 'none';
118}
119
120# usage($exitcode).
121# Prints a usage message and exits with $exitcode.
122sub usage {
123 my ($exitcode) = @_;
124 $exitcode = 1 unless defined $exitcode;
125 print <<'EOF';
126pintos, a utility for running Pintos in a simulator
127Usage: pintos [OPTION...] -- [ARGUMENT...]
128where each OPTION is one of the following options
129 and each ARGUMENT is passed to Pintos kernel verbatim.
130Simulator selection:
131 --bochs (default) Use Bochs as simulator
132 --qemu Use QEMU as simulator
133 --player Use VMware Player as simulator
134Debugger selection:
135 --no-debug (default) No debugger
136 --monitor Debug with simulator's monitor
137 --gdb Debug with gdb
138Display options: (default is both VGA and serial)
139 -v, --no-vga No VGA display or keyboard
140 -s, --no-serial No serial input or output
141 -t, --terminal Display VGA in terminal (Bochs only)
142Timing options: (Bochs only)
143 -j SEED Randomize timer interrupts
144 -r, --realtime Use realistic, not reproducible, timings
145Testing options:
146 -T, --timeout=N Kill Pintos after N seconds CPU time or N*load_avg
147 seconds wall-clock time (whichever comes first)
148 -k, --kill-on-failure Kill Pintos a few seconds after a kernel or user
149 panic, test failure, or triple fault
150 --kernel-test Run kernel test, even though user programs are
151 enabled.
152Configuration options:
153 -m, --mem=N Give Pintos N MB physical RAM (default: 4)
154File system commands:
155 -p, --put-file=HOSTFN Copy HOSTFN into VM, by default under same name
156 -g, --get-file=GUESTFN Copy GUESTFN out of VM, by default under same name
157 -a, --as=FILENAME Specifies guest (for -p) or host (for -g) file name
158Partition options: (where PARTITION is one of: kernel filesys scratch swap)
159 --PARTITION=FILE Use a copy of FILE for the given PARTITION
160 --PARTITION-size=SIZE Create an empty PARTITION of the given SIZE in MB
161 --PARTITION-from=DISK Use of a copy of the given PARTITION in DISK
162 (There is no --kernel-size, --scratch, or --scratch-from option.)
163Disk configuration options:
164 --make-disk=DISK Name the new DISK and don't delete it after the run
165 --disk=DISK Also use existing DISK (may be used multiple times)
166Advanced disk configuration options:
167 --loader=FILE Use FILE as bootstrap loader (default: loader.bin)
168 --geometry=H,S Use H head, S sector geometry (default: 16,63)
169 --geometry=zip Use 64 head, 32 sector geometry for USB-ZIP boot
170 (see http://syslinux.zytor.com/usbkey.php)
171 --align=bochs Pad out disk to cylinder to support Bochs (default)
172 --align=full Align partition boundaries to cylinder boundary to
173 let fdisk guess correct geometry and quiet warnings
174 --align=none Don't align partitions at all, to save space
175Other options:
176 -h, --help Display this help message.
177EOF
178 exit $exitcode;
179}
180
181# Sets the simulator.
182sub set_sim {
183 my ($new_sim) = @_;
184 die "--$new_sim conflicts with --$sim\n"
185 if defined ($sim) && $sim ne $new_sim;
186 $sim = $new_sim;
187}
188
189# Sets the debugger.
190sub set_debug {
191 my ($new_debug) = @_;
192 die "--$new_debug conflicts with --$debug\n"
193 if $debug ne 'none' && $new_debug ne 'none' && $debug ne $new_debug;
194 $debug = $new_debug;
195}
196
197# Sets VGA output destination.
198sub set_vga {
199 my ($new_vga) = @_;
200 if (defined ($vga) && $vga ne $new_vga) {
201 print "warning: conflicting vga display options\n";
202 }
203 $vga = $new_vga;
204}
205
206# Sets randomized timer interrupts.
207sub set_jitter {
208 my ($new_jitter) = @_;
209 die "--realtime conflicts with --jitter\n" if defined $realtime;
210 die "different --jitter already defined\n"
211 if defined $jitter && $jitter != $new_jitter;
212 $jitter = $new_jitter;
213}
214
215# Sets real-time timer interrupts.
216sub set_realtime {
217 die "--realtime conflicts with --jitter\n" if defined $jitter;
218 $realtime = 1;
219}
220
221# Sets load to run kernel test instead of user program.
222# If user programs are disabled, pintos always runs a kernel test.
223sub set_kernel_test {
224 $kernel_test = 1;
225}
226
227# add_file(\@list, $file)
228#
229# Adds [$file] to @list, which should be @puts or @gets.
230# Sets $as_ref to point to the added element.
231sub add_file {
232 my ($list, $file) = @_;
233 $as_ref = [$file];
234 push (@$list, $as_ref);
235}
236
237# Sets the guest/host name for the previous put/get.
238sub set_as {
239 my ($as) = @_;
240 die "-a (or --as) is only allowed after -p or -g\n" if !defined $as_ref;
241 die "Only one -a (or --as) is allowed after -p or -g\n"
242 if defined $as_ref->[1];
243 $as_ref->[1] = $as;
244}
245
246# Sets $disk as a disk to be included in the VM to run.
247sub set_disk {
248 my ($disk) = @_;
249
250 push (@disks, $disk);
251
252 my (%pt) = read_partition_table ($disk);
253 for my $role (keys %pt) {
254 die "can't have two sources for \L$role\E partition"
255 if exists $parts{$role};
256 $parts{$role}{DISK} = $disk;
257 $parts{$role}{START} = $pt{$role}{START};
258 $parts{$role}{SECTORS} = $pt{$role}{SECTORS};
259 }
260}
261
262# Locates the files used to back each of the virtual disks,
263# and creates temporary disks.
264sub find_disks {
265 # Find kernel, if we don't already have one.
266 if (!exists $parts{KERNEL}) {
267 my $name = find_file ('kernel.bin');
268 die "Cannot find kernel\n" if !defined $name;
269 do_set_part ('KERNEL', 'file', $name);
270 }
271
272 # Try to find file system and swap disks, if we don't already have
273 # partitions.
274 if (!exists $parts{FILESYS}) {
275 my $name = find_file ('filesys.dsk');
276 set_disk ($name) if defined $name;
277 }
278 if (!exists $parts{SWAP}) {
279 my $name = find_file ('swap.dsk');
280 set_disk ($name) if defined $name;
281 }
282
283 # Warn about (potentially) missing partitions.
284 if (my ($project) = `pwd` =~ /\b(threads|userprog|vm|filesys)\b/) {
285 if ((grep ($project eq $_, qw (userprog vm filesys)))
286 && !defined $parts{FILESYS}) {
287 print STDERR "warning: it looks like you're running the $project ";
288 print STDERR "project, but no file system partition is present\n";
289 }
290 if ($project eq 'vm' && !defined $parts{SWAP}) {
291 print STDERR "warning: it looks like you're running the $project ";
292 print STDERR "project, but no swap partition is present\n";
293 }
294 }
295
296 # Open disk handle.
297 my ($handle);
298 if (!defined $make_disk) {
299 ($handle, $make_disk) = tempfile (UNLINK => $tmp_disk,
300 SUFFIX => '.dsk');
301 } else {
302 die "$make_disk: already exists\n" if -e $make_disk;
303 open ($handle, '>', $make_disk) or die "$make_disk: create: $!\n";
304 }
305
306 # Prepare the arguments to pass to the Pintos kernel.
307 my (@args);
308 push (@args, '-kernel-test') if $kernel_test;
309 push (@args, shift (@kernel_args))
310 while @kernel_args && $kernel_args[0] =~ /^-/;
311 push (@args, 'extract') if @puts;
312 push (@args, @kernel_args);
313 push (@args, 'append', $_->[0]) foreach @gets;
314
315 # Make disk.
316 my (%disk);
317 our (@role_order);
318 for my $role (@role_order) {
319 my $p = $parts{$role};
320 next if !defined $p;
321 next if exists $p->{DISK};
322 $disk{$role} = $p;
323 }
324 $disk{DISK} = $make_disk;
325 $disk{HANDLE} = $handle;
326 $disk{ALIGN} = $align;
327 $disk{GEOMETRY} = %geometry;
328 $disk{FORMAT} = 'partitioned';
329 $disk{LOADER} = read_loader ($loader_fn);
330 $disk{ARGS} = \@args;
331 assemble_disk (%disk);
332
333 # Put the disk at the front of the list of disks.
334 unshift (@disks, $make_disk);
335 die "can't use more than " . scalar (@disks) . "disks\n" if @disks > 4;
336}
337
338# Prepare the scratch disk for gets and puts.
339sub prepare_scratch_disk {
340 return if !@gets && !@puts;
341
342 my ($p) = $parts{SCRATCH};
343 # Create temporary partition and write the files to put to it,
344 # then write an end-of-archive marker.
345 my ($part_handle, $part_fn) = tempfile (UNLINK => 1, SUFFIX => '.part');
346 put_scratch_file ($_->[0], defined $_->[1] ? $_->[1] : $_->[0],
347 $part_handle, $part_fn)
348 foreach @puts;
349 write_fully ($part_handle, $part_fn, "\0" x 1024);
350
351 # Make sure the scratch disk is big enough to get big files
352 # and at least as big as any requested size.
353 my ($size) = round_up (max (@gets * 1024 * 1024, $p->{BYTES} || 0), 512);
354 extend_file ($part_handle, $part_fn, $size);
355 close ($part_handle);
356
357 if (exists $p->{DISK}) {
358 # Copy the scratch partition to the disk.
359 die "$p->{DISK}: scratch partition too small\n"
360 if $p->{SECTORS} * 512 < $size;
361
362 my ($disk_handle);
363 open ($part_handle, '<', $part_fn) or die "$part_fn: open: $!\n";
364 open ($disk_handle, '+<', $p->{DISK}) or die "$p->{DISK}: open: $!\n";
365 my ($start) = $p->{START} * 512;
366 sysseek ($disk_handle, $start, SEEK_SET) == $start
367 or die "$p->{DISK}: seek: $!\n";
368 copy_file ($part_handle, $part_fn, $disk_handle, $p->{DISK}, $size);
369 close ($disk_handle) or die "$p->{DISK}: close: $!\n";
370 close ($part_handle) or die "$part_fn: close: $!\n";
371 } else {
372 # Set $part_fn as the source for the scratch partition.
373 do_set_part ('SCRATCH', 'file', $part_fn);
374 }
375}
376
377# Read "get" files from the scratch disk.
378sub finish_scratch_disk {
379 return if !@gets;
380
381 # Open scratch partition.
382 my ($p) = $parts{SCRATCH};
383 my ($part_handle);
384 my ($part_fn) = $p->{DISK};
385 open ($part_handle, '<', $part_fn) or die "$part_fn: open: $!\n";
386 sysseek ($part_handle, $p->{START} * 512, SEEK_SET) == $p->{START} * 512
387 or die "$part_fn: seek: $!\n";
388
389 # Read each file.
390 # If reading fails, delete that file and all subsequent files, but
391 # don't die with an error, because that's a guest error not a host
392 # error. (If we do exit with an error code, it fouls up the
393 # grading process.) Instead, just make sure that the host file(s)
394 # we were supposed to retrieve is unlinked.
395 my ($ok) = 1;
396 my ($part_end) = ($p->{START} + $p->{SECTORS}) * 512;
397 foreach my $get (@gets) {
398 my ($name) = defined ($get->[1]) ? $get->[1] : $get->[0];
399 if ($ok) {
400 my ($error) = get_scratch_file ($name, $part_handle, $part_fn);
401 if (!$error && sysseek ($part_handle, 0, SEEK_CUR) > $part_end) {
402 $error = "$part_fn: scratch data overflows partition";
403 }
404 if ($error) {
405 print STDERR "getting $name failed ($error)\n";
406 $ok = 0;
407 }
408 }
409 die "$name: unlink: $!\n" if !$ok && !unlink ($name) && !$!{ENOENT};
410 }
411}
412
413# mk_ustar_field($number, $size)
414#
415# Returns $number in a $size-byte numeric field in the format used by
416# the standard ustar archive header.
417sub mk_ustar_field {
418 my ($number, $size) = @_;
419 my ($len) = $size - 1;
420 my ($out) = sprintf ("%0${len}o", $number) . "\0";
421 die "$number: too large for $size-byte octal ustar field\n"
422 if length ($out) != $size;
423 return $out;
424}
425
426# calc_ustar_chksum($s)
427#
428# Calculates and returns the ustar checksum of 512-byte ustar archive
429# header $s.
430sub calc_ustar_chksum {
431 my ($s) = @_;
432 die if length ($s) != 512;
433 substr ($s, 148, 8, ' ' x 8);
434 return unpack ("%32a*", $s);
435}
436
437# put_scratch_file($src_file_name, $dst_file_name,
438# $disk_handle, $disk_file_name).
439#
440# Copies $src_file_name into $disk_handle for extraction as
441# $dst_file_name. $disk_file_name is used for error messages.
442sub put_scratch_file {
443 my ($src_file_name, $dst_file_name, $disk_handle, $disk_file_name) = @_;
444
445 print "Copying $src_file_name to scratch partition...\n";
446
447 # ustar format supports up to 100 characters for a file name, and
448 # even longer names given some common properties, but our code in
449 # the Pintos kernel only supports at most 99 characters.
450 die "$dst_file_name: name too long (max 99 characters)\n"
451 if length ($dst_file_name) > 99;
452
453 # Compose and write ustar header.
454 stat $src_file_name or die "$src_file_name: stat: $!\n";
455 my ($size) = -s _;
456 my ($header) = (pack ("a100", $dst_file_name) # name
457 . mk_ustar_field (0644, 8) # mode
458 . mk_ustar_field (0, 8) # uid
459 . mk_ustar_field (0, 8) # gid
460 . mk_ustar_field ($size, 12) # size
461 . mk_ustar_field (1136102400, 12) # mtime
462 . (' ' x 8) # chksum
463 . '0' # typeflag
464 . ("\0" x 100) # linkname
465 . "ustar\0" # magic
466 . "00" # version
467 . "root" . ("\0" x 28) # uname
468 . "root" . ("\0" x 28) # gname
469 . "\0" x 8 # devmajor
470 . "\0" x 8 # devminor
471 . ("\0" x 155)) # prefix
472 . "\0" x 12; # pad to 512 bytes
473 substr ($header, 148, 8) = mk_ustar_field (calc_ustar_chksum ($header), 8);
474 write_fully ($disk_handle, $disk_file_name, $header);
475
476 # Copy file data.
477 my ($put_handle);
478 sysopen ($put_handle, $src_file_name, O_RDONLY)
479 or die "$src_file_name: open: $!\n";
480 copy_file ($put_handle, $src_file_name, $disk_handle, $disk_file_name,
481 $size);
482 die "$src_file_name: changed size while being read\n"
483 if $size != -s $put_handle;
484 close ($put_handle);
485
486 # Round up disk data to beginning of next sector.
487 write_fully ($disk_handle, $disk_file_name, "\0" x (512 - $size % 512))
488 if $size % 512;
489}
490
491# get_scratch_file($get_file_name, $disk_handle, $disk_file_name)
492#
493# Copies from $disk_handle to $get_file_name (which is created).
494# $disk_file_name is used for error messages.
495# Returns 1 if successful, 0 on failure.
496sub get_scratch_file {
497 my ($get_file_name, $disk_handle, $disk_file_name) = @_;
498
499 print "Copying $get_file_name out of $disk_file_name...\n";
500
501 # Read ustar header sector.
502 my ($header) = read_fully ($disk_handle, $disk_file_name, 512);
503 return "scratch disk tar archive ends unexpectedly"
504 if $header eq ("\0" x 512);
505
506 # Verify magic numbers.
507 return "corrupt ustar signature" if substr ($header, 257, 6) ne "ustar\0";
508 return "invalid ustar version" if substr ($header, 263, 2) ne '00';
509
510 # Verify checksum.
511 my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8)));
512 my ($correct_chksum) = calc_ustar_chksum ($header);
513 return "checksum mismatch" if $chksum != $correct_chksum;
514
515 # Get type.
516 my ($typeflag) = substr ($header, 156, 1);
517 return "not a regular file" if $typeflag ne '0' && $typeflag ne "\0";
518
519 # Get size.
520 my ($size) = oct (unpack ("Z*", substr ($header, 124, 12)));
521 return "bad size $size\n" if $size < 0;
522
523 # Copy file data.
524 my ($get_handle);
525 sysopen ($get_handle, $get_file_name, O_WRONLY | O_CREAT, 0666)
526 or die "$get_file_name: create: $!\n";
527 copy_file ($disk_handle, $disk_file_name, $get_handle, $get_file_name,
528 $size);
529 close ($get_handle);
530
531 # Skip forward in disk up to beginning of next sector.
532 read_fully ($disk_handle, $disk_file_name, 512 - $size % 512)
533 if $size % 512;
534
535 return 0;
536}
537
538# Running simulators.
539
540# Runs the selected simulator.
541sub run_vm {
542 if ($sim eq 'bochs') {
543 run_bochs ();
544 } elsif ($sim eq 'qemu') {
545 run_qemu ();
546 } elsif ($sim eq 'player') {
547 run_player ();
548 } else {
549 die "unknown simulator `$sim'\n";
550 }
551}
552
553# Runs Bochs.
554sub run_bochs {
555 # Select Bochs binary based on the chosen debugger.
556 my ($bin) = $debug eq 'monitor' ? 'bochs-dbg' : 'bochs';
557
558 my ($squish_pty);
559 if ($serial) {
560 $squish_pty = find_in_path ("squish-pty");
561 print "warning: can't find squish-pty, so terminal input will fail\n"
562 if !defined $squish_pty;
563 }
564
565 # Write bochsrc.txt configuration file.
566 open (BOCHSRC, ">", "bochsrc.txt") or die "bochsrc.txt: create: $!\n";
567 print BOCHSRC <<EOF;
568romimage: file=\$BXSHARE/BIOS-bochs-latest
569vgaromimage: file=\$BXSHARE/VGABIOS-lgpl-latest
570boot: disk
571cpu: ips=1000000
572megs: $mem
573log: bochsout.txt
574panic: action=fatal
575user_shortcut: keys=ctrlaltdel
576EOF
577 print BOCHSRC "gdbstub: enabled=1\n" if $debug eq 'gdb';
578 print BOCHSRC "clock: sync=", $realtime ? 'realtime' : 'none',
579 ", time0=0\n";
580 print BOCHSRC "ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15\n"
581 if @disks > 2;
582 print_bochs_disk_line ("ata0-master", $disks[0]);
583 print_bochs_disk_line ("ata0-slave", $disks[1]);
584 print_bochs_disk_line ("ata1-master", $disks[2]);
585 print_bochs_disk_line ("ata1-slave", $disks[3]);
586 if ($vga ne 'terminal') {
587 if ($serial) {
588 my $mode = defined ($squish_pty) ? "term" : "file";
589 print BOCHSRC "com1: enabled=1, mode=$mode, dev=/dev/stdout\n";
590 }
591 print BOCHSRC "display_library: nogui\n" if $vga eq 'none';
592 } else {
593 print BOCHSRC "display_library: term\n";
594 }
595 close (BOCHSRC);
596
597 # Compose Bochs command line.
598 my (@cmd) = ($bin, '-q');
599 unshift (@cmd, $squish_pty) if defined $squish_pty;
600 push (@cmd, '-j', $jitter) if defined $jitter;
601
602 # Run Bochs.
603 print join (' ', @cmd), "\n";
604 my ($exit) = xsystem (@cmd);
605 if (WIFEXITED ($exit)) {
606 # Bochs exited normally.
607 # Ignore the exit code; Bochs normally exits with status 1,
608 # which is weird.
609 } elsif (WIFSIGNALED ($exit)) {
610 die "Bochs died with signal ", WTERMSIG ($exit), "\n";
611 } else {
612 die "Bochs died: code $exit\n";
613 }
614}
615
616sub print_bochs_disk_line {
617 my ($device, $disk) = @_;
618 if (defined $disk) {
619 my (%geom) = disk_geometry ($disk);
620 print BOCHSRC "$device: type=disk, path=$disk, mode=flat, ";
621 print BOCHSRC "cylinders=$geom{C}, heads=$geom{H}, spt=$geom{S}, ";
622 print BOCHSRC "translation=none\n";
623 }
624}
625
626# Runs QEMU.
627sub run_qemu {
628 print "warning: qemu doesn't support --terminal\n"
629 if $vga eq 'terminal';
630 print "warning: qemu doesn't support jitter\n"
631 if defined $jitter;
632 my (@cmd) = ('qemu');
633# push (@cmd, '-no-kqemu');
634 push (@cmd, '-hda', $disks[0]) if defined $disks[0];
635 push (@cmd, '-hdb', $disks[1]) if defined $disks[1];
636 push (@cmd, '-hdc', $disks[2]) if defined $disks[2];
637 push (@cmd, '-hdd', $disks[3]) if defined $disks[3];
638 push (@cmd, '-m', $mem);
639 push (@cmd, '-net', 'none');
640 push (@cmd, '-nographic') if $vga eq 'none';
641 push (@cmd, '-serial', 'stdio') if $serial && $vga ne 'none';
642 push (@cmd, '-S') if $debug eq 'monitor';
643 push (@cmd, '-s', '-S') if $debug eq 'gdb';
644 push (@cmd, '-monitor', 'null') if $vga eq 'none' && $debug eq 'none';
645 run_command (@cmd);
646}
647
648# player_unsup($flag)
649#
650# Prints a message that $flag is unsupported by VMware Player.
651sub player_unsup {
652 my ($flag) = @_;
653 print "warning: no support for $flag with VMware Player\n";
654}
655
656# Runs VMware Player.
657sub run_player {
658 player_unsup ("--$debug") if $debug ne 'none';
659 player_unsup ("--no-vga") if $vga eq 'none';
660 player_unsup ("--terminal") if $vga eq 'terminal';
661 player_unsup ("--jitter") if defined $jitter;
662 player_unsup ("--timeout"), undef $timeout if defined $timeout;
663 player_unsup ("--kill-on-failure"), undef $kill_on_failure
664 if defined $kill_on_failure;
665
666 $mem = round_up ($mem, 4); # Memory must be multiple of 4 MB.
667
668 open (VMX, ">", "pintos.vmx") or die "pintos.vmx: create: $!\n";
669 chmod 0777 & ~umask, "pintos.vmx";
670 print VMX <<EOF;
671#! /usr/bin/vmware -G
672config.version = 8
673guestOS = "linux"
674memsize = $mem
675floppy0.present = FALSE
676usb.present = FALSE
677sound.present = FALSE
678gui.exitAtPowerOff = TRUE
679gui.exitOnCLIHLT = TRUE
680gui.powerOnAtStartUp = TRUE
681EOF
682
683 print VMX <<EOF if $serial;
684serial0.present = TRUE
685serial0.fileType = "pipe"
686serial0.fileName = "pintos.socket"
687serial0.pipe.endPoint = "client"
688serial0.tryNoRxLoss = "TRUE"
689EOF
690
691 for (my ($i) = 0; $i < 4; $i++) {
692 my ($dsk) = $disks[$i];
693 last if !defined $dsk;
694
695 my ($device) = "ide" . int ($i / 2) . ":" . ($i % 2);
696 my ($pln) = "$device.pln";
697 print VMX <<EOF;
698
699$device.present = TRUE
700$device.deviceType = "plainDisk"
701$device.fileName = "$pln"
702EOF
703
704 open (URANDOM, '<', '/dev/urandom') or die "/dev/urandom: open: $!\n";
705 my ($bytes);
706 sysread (URANDOM, $bytes, 4) == 4 or die "/dev/urandom: read: $!\n";
707 close (URANDOM);
708 my ($cid) = unpack ("L", $bytes);
709
710 my (%geom) = disk_geometry ($dsk);
711 open (PLN, ">", $pln) or die "$pln: create: $!\n";
712 print PLN <<EOF;
713version=1
714CID=$cid
715parentCID=ffffffff
716createType="monolithicFlat"
717
718RW $geom{CAPACITY} FLAT "$dsk" 0
719
720# The Disk Data Base
721#DDB
722
723ddb.adapterType = "ide"
724ddb.virtualHWVersion = "4"
725ddb.toolsVersion = "2"
726ddb.geometry.cylinders = "$geom{C}"
727ddb.geometry.heads = "$geom{H}"
728ddb.geometry.sectors = "$geom{S}"
729EOF
730 close (PLN);
731 }
732 close (VMX);
733
734 my ($squish_unix);
735 if ($serial) {
736 $squish_unix = find_in_path ("squish-unix");
737 print "warning: can't find squish-unix, so terminal input ",
738 "and output will fail\n" if !defined $squish_unix;
739 }
740
741 my ($vmx) = getcwd () . "/pintos.vmx";
742 my (@cmd) = ("vmplayer", $vmx);
743 unshift (@cmd, $squish_unix, "pintos.socket") if $squish_unix;
744 print join (' ', @cmd), "\n";
745 xsystem (@cmd);
746}
747
748# Disk utilities.
749
750sub extend_file {
751 my ($handle, $file_name, $size) = @_;
752 if (-s ($handle) < $size) {
753 sysseek ($handle, $size - 1, 0) == $size - 1
754 or die "$file_name: seek: $!\n";
755 syswrite ($handle, "\0") == 1
756 or die "$file_name: write: $!\n";
757 }
758}
759
760# disk_geometry($file)
761#
762# Examines $file and returns a valid IDE disk geometry for it, as a
763# hash.
764sub disk_geometry {
765 my ($file) = @_;
766 my ($size) = -s $file;
767 die "$file: stat: $!\n" if !defined $size;
768 die "$file: size $size not a multiple of 512 bytes\n" if $size % 512;
769 my ($cyl_size) = 512 * 16 * 63;
770 my ($cylinders) = ceil ($size / $cyl_size);
771
772 return (CAPACITY => $size / 512,
773 C => $cylinders,
774 H => 16,
775 S => 63);
776}
777
778# Subprocess utilities.
779
780# run_command(@args)
781#
782# Runs xsystem(@args).
783# Also prints the command it's running and checks that it succeeded.
784sub run_command {
785 print join (' ', @_), "\n";
786 die "command failed\n" if xsystem (@_);
787}
788
789# xsystem(@args)
790#
791# Creates a subprocess via exec(@args) and waits for it to complete.
792# Relays common signals to the subprocess.
793# If $timeout is set then the subprocess will be killed after that long.
794sub xsystem {
795 # QEMU turns off local echo and does not restore it if killed by a signal.
796 # We compensate by restoring it ourselves.
797 my $cleanup = sub {};
798 if (isatty (0)) {
799 my $termios = POSIX::Termios->new;
800 $termios->getattr (0);
801 $cleanup = sub { $termios->setattr (0, &POSIX::TCSANOW); }
802 }
803
804 # Create pipe for filtering output.
805 pipe (my $in, my $out) or die "pipe: $!\n" if $kill_on_failure;
806
807 my ($pid) = fork;
808 if (!defined ($pid)) {
809 # Fork failed.
810 die "fork: $!\n";
811 } elsif (!$pid) {
812 # Running in child process.
813 dup2 (fileno ($out), STDOUT_FILENO) or die "dup2: $!\n"
814 if $kill_on_failure;
815 exec_setitimer (@_);
816 } else {
817 # Running in parent process.
818 close $out if $kill_on_failure;
819
820 my ($cause);
821 local $SIG{ALRM} = sub { timeout ($pid, $cause, $cleanup); };
822 local $SIG{INT} = sub { relay_signal ($pid, "INT", $cleanup); };
823 local $SIG{TERM} = sub { relay_signal ($pid, "TERM", $cleanup); };
824 alarm ($timeout * get_load_average () + 1) if defined ($timeout);
825
826 if ($kill_on_failure) {
827 # Filter output.
828 my ($buf) = "";
829 my ($boots) = 0;
830 local ($|) = 1;
831 for (;;) {
832 if (waitpid ($pid, WNOHANG) != 0) {
833 # Subprocess died. Pass through any remaining data.
834 print $buf while sysread ($in, $buf, 4096) > 0;
835 last;
836 }
837
838 # Read and print out pipe data.
839 my ($len) = length ($buf);
840 waitpid ($pid, 0), last
841 if sysread ($in, $buf, 4096, $len) <= 0;
842 print substr ($buf, $len);
843
844 # Remove full lines from $buf and scan them for keywords.
845 while ((my $idx = index ($buf, "\n")) >= 0) {
846 local $_ = substr ($buf, 0, $idx + 1, '');
847 next if defined ($cause);
848 if (/(Kernel PANIC|User process ABORT)/ ) {
849 $cause = "\L$1\E";
850 alarm (5);
851 } elsif (/Pintos booting/ && ++$boots > 1) {
852 $cause = "triple fault";
853 alarm (5);
854 } elsif (/FAILED/) {
855 $cause = "test failure";
856 alarm (5);
857 }
858 }
859 }
860 } else {
861 waitpid ($pid, 0);
862 }
863 alarm (0);
864 &$cleanup ();
865
866 if (WIFSIGNALED ($?) && WTERMSIG ($?) == SIGVTALRM ()) {
867 seek (STDOUT, 0, 2);
868 print "\nTIMEOUT after $timeout seconds of host CPU time\n";
869 exit 0;
870 }
871
872 return $?;
873 }
874}
875
876# relay_signal($pid, $signal, &$cleanup)
877#
878# Relays $signal to $pid and then reinvokes it for us with the default
879# handler. Also cleans up temporary files and invokes $cleanup.
880sub relay_signal {
881 my ($pid, $signal, $cleanup) = @_;
882 kill $signal, $pid;
883 eval { File::Temp::cleanup() }; # Not defined in old File::Temp.
884 &$cleanup ();
885 $SIG{$signal} = 'DEFAULT';
886 kill $signal, getpid ();
887}
888
889# timeout($pid, $cause, &$cleanup)
890#
891# Interrupts $pid and dies with a timeout error message,
892# after invoking $cleanup.
893sub timeout {
894 my ($pid, $cause, $cleanup) = @_;
895 kill "INT", $pid;
896 waitpid ($pid, 0);
897 &$cleanup ();
898 seek (STDOUT, 0, 2);
899 if (!defined ($cause)) {
900 my ($load_avg) = `uptime` =~ /(load average:.*)$/i;
901 print "\nTIMEOUT after ", time () - $start_time,
902 " seconds of wall-clock time";
903 print " - $load_avg" if defined $load_avg;
904 print "\n";
905 } else {
906 print "Simulation terminated due to $cause.\n";
907 }
908 exit 0;
909}
910
911# Returns the system load average over the last minute.
912# If the load average is less than 1.0 or cannot be determined, returns 1.0.
913sub get_load_average {
914 my ($avg) = `uptime` =~ /load average:\s*([^,]+),/;
915 return $avg >= 1.0 ? $avg : 1.0;
916}
917
918# Calls setitimer to set a timeout, then execs what was passed to us.
919sub exec_setitimer {
920 if (defined $timeout) {
921 if ($ ge 5.8.0) {
922 eval "
923 use Time::HiRes qw(setitimer ITIMER_VIRTUAL);
924 setitimer (ITIMER_VIRTUAL, $timeout, 0);
925 ";
926 } else {
927 { exec ("setitimer-helper", $timeout, @_); };
928 exit 1 if !$!{ENOENT};
929 print STDERR "warning: setitimer-helper is not installed, so ",
930 "CPU time limit will not be enforced\n";
931 }
932 }
933 exec (@_);
934 exit (1);
935}
936
937sub SIGVTALRM {
938 use Config;
939 my $i = 0;
940 foreach my $name (split(' ', $Config{sig_name})) {
941 return $i if $name eq 'VTALRM';
942 $i++;
943 }
944 return 0;
945}
946
947# find_in_path ($program)
948#
949# Searches for $program in $ENV{PATH}.
950# Returns $program if found, otherwise undef.
951sub find_in_path {
952 my ($program) = @_;
953 -x "$_/$program" and return $program foreach split (':', $ENV{PATH});
954 return;
955}
diff --git a/pintos-progos/utils/pintos-gdb b/pintos-progos/utils/pintos-gdb
new file mode 100755
index 0000000..9c9555b
--- /dev/null
+++ b/pintos-progos/utils/pintos-gdb
@@ -0,0 +1,21 @@
1#! /bin/sh
2
3# Path to GDB macros file. Customize for your site.
4PINTOS_SRC="$(dirname $(dirname $(which pintos-gdb)))"
5GDBMACROS="${PINTOS_SRC}/misc/gdb-macros"
6
7# Choose correct GDB.
8if command -v i386-elf-gdb >/dev/null 2>&1; then
9 GDB=i386-elf-gdb
10else
11 GDB=gdb
12fi
13
14# Run GDB.
15if test -f "$GDBMACROS"; then
16 exec $GDB -x "$GDBMACROS" "$@"
17else
18 echo "*** $GDBMACROS does not exist ***"
19 echo "*** Pintos GDB macros will not be available ***"
20 exec $GDB "$@"
21fi
diff --git a/pintos-progos/utils/pintos-mkdisk b/pintos-progos/utils/pintos-mkdisk
new file mode 100755
index 0000000..87b1563
--- /dev/null
+++ b/pintos-progos/utils/pintos-mkdisk
@@ -0,0 +1,134 @@
1#! /usr/bin/perl
2
3use strict;
4use warnings;
5use POSIX;
6use Getopt::Long qw(:config bundling);
7use Fcntl 'SEEK_SET';
8
9# Read Pintos.pm from the same directory as this program.
10BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; }
11
12our ($disk_fn); # Output disk file name.
13our (%parts); # Partitions.
14our ($format); # "partitioned" (default) or "raw"
15our (%geometry); # IDE disk geometry.
16our ($align); # Align partitions on cylinders?
17our ($loader_fn); # File name of loader.
18our ($include_loader); # Include loader?
19our (@kernel_args); # Kernel arguments.
20
21if (grep ($_ eq '--', @ARGV)) {
22 @kernel_args = @ARGV;
23 @ARGV = ();
24 while ((my $arg = shift (@kernel_args)) ne '--') {
25 push (@ARGV, $arg);
26 }
27}
28
29GetOptions ("h|help" => sub { usage (0); },
30
31 "kernel=s" => \&set_part,
32 "filesys=s" => \&set_part,
33 "scratch=s" => \&set_part,
34 "swap=s" => \&set_part,
35
36 "filesys-size=s" => \&set_part,
37 "scratch-size=s" => \&set_part,
38 "swap-size=s" => \&set_part,
39
40 "kernel-from=s" => \&set_part,
41 "filesys-from=s" => \&set_part,
42 "scratch-from=s" => \&set_part,
43 "swap-from=s" => \&set_part,
44
45 "format=s" => \$format,
46 "loader:s" => \&set_loader,
47 "no-loader" => \&set_no_loader,
48 "geometry=s" => \&set_geometry,
49 "align=s" => \&set_align)
50 or exit 1;
51usage (1) if @ARGV != 1;
52
53$disk_fn = $ARGV[0];
54die "$disk_fn: already exists\n" if -e $disk_fn;
55
56# Sets the loader to copy to the MBR.
57sub set_loader {
58 die "can't specify both --loader and --no-loader\n"
59 if defined ($include_loader) && !$include_loader;
60 $include_loader = 1;
61 $loader_fn = $_[1] if $_[1] ne '';
62}
63
64# Disables copying a loader to the MBR.
65sub set_no_loader {
66 die "can't specify both --loader and --no-loader\n"
67 if defined ($include_loader) && $include_loader;
68 $include_loader = 0;
69}
70
71# Figure out whether to include a loader.
72$include_loader = exists ($parts{KERNEL}) && $format eq 'partitioned'
73 if !defined ($include_loader);
74die "can't write loader to raw disk\n" if $include_loader && $format eq 'raw';
75die "can't write command-line arguments without --loader or --kernel\n"
76 if @kernel_args && !$include_loader;
77print STDERR "warning: --loader only makes sense without --kernel "
78 . "if this disk will be used to load a kernel from another disk\n"
79 if $include_loader && !exists ($parts{KERNEL});
80
81# Open disk.
82my ($disk_handle);
83open ($disk_handle, '>', $disk_fn) or die "$disk_fn: create: $!\n";
84
85# Read loader.
86my ($loader);
87$loader = read_loader ($loader_fn) if $include_loader;
88
89# Write disk.
90my (%disk) = %parts;
91$disk{DISK} = $disk_fn;
92$disk{HANDLE} = $disk_handle;
93$disk{ALIGN} = $align;
94$disk{GEOMETRY} = %geometry;
95$disk{FORMAT} = $format;
96$disk{LOADER} = $loader;
97$disk{ARGS} = \@kernel_args;
98assemble_disk (%disk);
99
100# Done.
101exit 0;
102
103sub usage {
104 print <<'EOF';
105pintos-mkdisk, a utility for creating Pintos virtual disks
106Usage: pintos-mkdisk [OPTIONS] DISK [-- ARGUMENT...]
107where DISK is the virtual disk to create,
108 each ARGUMENT is inserted into the command line written to DISK,
109 and each OPTION is one of the following options.
110Partition options: (where PARTITION is one of: kernel filesys scratch swap)
111 --PARTITION=FILE Use a copy of FILE for the given PARTITION
112 --PARTITION-size=SIZE Create an empty PARTITION of the given SIZE in MB
113 --PARTITION-from=DISK Use of a copy of the given PARTITION in DISK
114 (There is no --kernel-size option.)
115Output disk options:
116 --format=partitioned Write partition table to output (default)
117 --format=raw Do not write partition table to output
118 (Pintos can only use partitioned disks.)
119Partitioned format output options:
120 --loader[=FILE] Get bootstrap loader from FILE (default: loader.bin
121 if --kernel option is specified, empty otherwise)
122 --no-loader Do not include a bootstrap loader
123 --geometry=H,S Use H head, S sector geometry (default: 16, 63)
124 --geometry=zip Use 64 head, 32 sector geometry for USB-ZIP boot
125 per http://syslinux.zytor.com/usbkey.php
126 --align=bochs Round size to cylinder for Bochs support (default)
127 --align=full Align partition boundaries to cylinder boundary to
128 let fdisk guess correct geometry and quiet warnings
129 --align=none Don't align partitions at all, to save space
130Other options:
131 -h, --help Display this help message.
132EOF
133 exit ($_[0]);
134}
diff --git a/pintos-progos/utils/pintos-set-cmdline b/pintos-progos/utils/pintos-set-cmdline
new file mode 100644
index 0000000..8c8f702
--- /dev/null
+++ b/pintos-progos/utils/pintos-set-cmdline
@@ -0,0 +1,42 @@
1#! /usr/bin/perl -w
2
3use strict;
4use Fcntl 'SEEK_SET';
5
6# Read Pintos.pm from the same directory as this program.
7BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; }
8
9# Get command-line arguments.
10usage (0) if @ARGV == 1 && $ARGV[0] eq '--help';
11usage (1) if @ARGV < 2 || $ARGV[1] ne '--';
12my ($disk, undef, @kernel_args) = @ARGV;
13
14# Open disk.
15my ($handle);
16open ($handle, '+<', $disk) or die "$disk: open: $!\n";
17
18# Check that it's a partitioned disk with a Pintos loader.
19my ($buffer) = read_fully ($handle, $disk, 512);
20unpack ("x510 v", $buffer) == 0xaa55 or die "$disk: not a partitioned disk\n";
21$buffer =~ /Pintos/ or die "$disk: does not contain Pintos loader\n";
22
23# Write the command line.
24our ($LOADER_SIZE);
25sysseek ($handle, $LOADER_SIZE, SEEK_SET) == $LOADER_SIZE
26 or die "$disk: seek: $!\n";
27write_fully ($handle, $disk, make_kernel_command_line (@kernel_args));
28
29# Close disk.
30close ($handle) or die "$disk: close: $!\n";
31
32exit 0;
33
34sub usage {
35 print <<'EOF';
36pintos-set-cmdline, a utility for changing the command line in Pintos disks
37Usage: pintos-set-cmdline DISK -- [ARGUMENT...]
38where DISK is a bootable disk containing a Pintos loader
39 and each ARGUMENT is inserted into the command line written to DISK.
40EOF
41 exit ($_[0]);
42}
diff --git a/pintos-progos/utils/setitimer-helper.c b/pintos-progos/utils/setitimer-helper.c
new file mode 100644
index 0000000..772d736
--- /dev/null
+++ b/pintos-progos/utils/setitimer-helper.c
@@ -0,0 +1,49 @@
1#include <errno.h>
2#include <limits.h>
3#include <math.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <sys/time.h>
8#include <unistd.h>
9
10int
11main (int argc, char *argv[])
12{
13 const char *program_name = argv[0];
14 double timeout;
15
16 if (argc < 3)
17 {
18 fprintf (stderr,
19 "setitimer-helper: runs a program with a virtual CPU limit\n"
20 "usage: %s TIMEOUT PROGRAM [ARG...]\n"
21 " where TIMEOUT is the virtual CPU limit, in seconds,\n"
22 " and remaining arguments specify the program to run\n"
23 " and its argument.\n",
24 program_name);
25 return EXIT_FAILURE;
26 }
27
28 timeout = strtod (argv[1], NULL);
29 if (timeout >= 0.0 && timeout < LONG_MAX)
30 {
31 struct itimerval it;
32
33 it.it_interval.tv_sec = 0;
34 it.it_interval.tv_usec = 0;
35 it.it_value.tv_sec = timeout;
36 it.it_value.tv_usec = (timeout - floor (timeout)) * 1000000;
37 if (setitimer (ITIMER_VIRTUAL, &it, NULL) < 0)
38 fprintf (stderr, "%s: setitimer: %s\n",
39 program_name, strerror (errno));
40 }
41 else
42 fprintf (stderr, "%s: invalid timeout value \"%s\"\n",
43 program_name, argv[1]);
44
45 execvp (argv[2], &argv[2]);
46 fprintf (stderr, "%s: couldn't exec \"%s\": %s\n",
47 program_name, argv[2], strerror (errno));
48 return EXIT_FAILURE;
49}
diff --git a/pintos-progos/utils/squish-pty.c b/pintos-progos/utils/squish-pty.c
new file mode 100644
index 0000000..c8375a5
--- /dev/null
+++ b/pintos-progos/utils/squish-pty.c
@@ -0,0 +1,355 @@
1#define _GNU_SOURCE 1
2#include <errno.h>
3#include <fcntl.h>
4#include <signal.h>
5#include <stdarg.h>
6#include <stdbool.h>
7#include <stdio.h>
8#include <stdlib.h>
9#include <string.h>
10#include <stropts.h>
11#include <sys/ioctl.h>
12#include <sys/stat.h>
13#include <sys/time.h>
14#include <sys/types.h>
15#include <sys/wait.h>
16#include <termios.h>
17#include <unistd.h>
18
19static void
20fail_io (const char *msg, ...)
21 __attribute__ ((noreturn))
22 __attribute__ ((format (printf, 1, 2)));
23
24/* Prints MSG, formatting as with printf(),
25 plus an error message based on errno,
26 and exits. */
27static void
28fail_io (const char *msg, ...)
29{
30 va_list args;
31
32 va_start (args, msg);
33 vfprintf (stderr, msg, args);
34 va_end (args);
35
36 if (errno != 0)
37 fprintf (stderr, ": %s", strerror (errno));
38 putc ('\n', stderr);
39 exit (EXIT_FAILURE);
40}
41
42/* If FD is a terminal, configures it for noncanonical input mode
43 with VMIN and VTIME set as indicated.
44 If FD is not a terminal, has no effect. */
45static void
46make_noncanon (int fd, int vmin, int vtime)
47{
48 if (isatty (fd))
49 {
50 struct termios termios;
51 if (tcgetattr (fd, &termios) < 0)
52 fail_io ("tcgetattr");
53 termios.c_lflag &= ~(ICANON | ECHO);
54 termios.c_cc[VMIN] = vmin;
55 termios.c_cc[VTIME] = vtime;
56 if (tcsetattr (fd, TCSANOW, &termios) < 0)
57 fail_io ("tcsetattr");
58 }
59}
60
61/* Make FD non-blocking if NONBLOCKING is true,
62 or blocking if NONBLOCKING is false. */
63static void
64make_nonblocking (int fd, bool nonblocking)
65{
66 int flags = fcntl (fd, F_GETFL);
67 if (flags < 0)
68 fail_io ("fcntl");
69 if (nonblocking)
70 flags |= O_NONBLOCK;
71 else
72 flags &= ~O_NONBLOCK;
73 if (fcntl (fd, F_SETFL, flags) < 0)
74 fail_io ("fcntl");
75}
76
77/* Handle a read or write on *FD, which is the pty if FD_IS_PTY
78 is true, that returned end-of-file or error indication RETVAL.
79 The system call is named CALL, for use in error messages.
80 Returns true if processing may continue, false if we're all
81 done. */
82static bool
83handle_error (ssize_t retval, int *fd, bool fd_is_pty, const char *call)
84{
85 if (fd_is_pty)
86 {
87 if (retval < 0)
88 {
89 if (errno == EIO)
90 {
91 /* Slave side of pty has been closed. */
92 return false;
93 }
94 else
95 fail_io (call);
96 }
97 else
98 return true;
99 }
100 else
101 {
102 if (retval == 0)
103 {
104 close (*fd);
105 *fd = -1;
106 return true;
107 }
108 else
109 fail_io (call);
110 }
111}
112
113/* Copies data from stdin to PTY and from PTY to stdout until no
114 more data can be read or written. */
115static void
116relay (int pty, int dead_child_fd)
117{
118 struct pipe
119 {
120 int in, out;
121 char buf[BUFSIZ];
122 size_t size, ofs;
123 bool active;
124 };
125 struct pipe pipes[2];
126
127 /* Make PTY, stdin, and stdout non-blocking. */
128 make_nonblocking (pty, true);
129 make_nonblocking (STDIN_FILENO, true);
130 make_nonblocking (STDOUT_FILENO, true);
131
132 /* Configure noncanonical mode on PTY and stdin to avoid
133 waiting for end-of-line. We want to minimize context
134 switching on PTY (for efficiency) and minimize latency on
135 stdin to avoid a laggy user experience. */
136 make_noncanon (pty, 16, 1);
137 make_noncanon (STDIN_FILENO, 1, 0);
138
139 memset (pipes, 0, sizeof pipes);
140 pipes[0].in = STDIN_FILENO;
141 pipes[0].out = pty;
142 pipes[1].in = pty;
143 pipes[1].out = STDOUT_FILENO;
144
145 while (pipes[0].in != -1 || pipes[1].in != -1)
146 {
147 fd_set read_fds, write_fds;
148 int retval;
149 int i;
150
151 FD_ZERO (&read_fds);
152 FD_ZERO (&write_fds);
153 for (i = 0; i < 2; i++)
154 {
155 struct pipe *p = &pipes[i];
156
157 /* Don't do anything with the stdin->pty pipe until we
158 have some data for the pty->stdout pipe. If we get
159 too eager, Bochs will throw away our input. */
160 if (i == 0 && !pipes[1].active)
161 continue;
162
163 if (p->in != -1 && p->size + p->ofs < sizeof p->buf)
164 FD_SET (p->in, &read_fds);
165 if (p->out != -1 && p->size > 0)
166 FD_SET (p->out, &write_fds);
167 }
168 FD_SET (dead_child_fd, &read_fds);
169
170 do
171 {
172 retval = select (FD_SETSIZE, &read_fds, &write_fds, NULL, NULL);
173 }
174 while (retval < 0 && errno == EINTR);
175 if (retval < 0)
176 fail_io ("select");
177
178 if (FD_ISSET (dead_child_fd, &read_fds))
179 {
180 /* Child died. Do final relaying. */
181 struct pipe *p = &pipes[1];
182 if (p->out == -1)
183 return;
184 make_nonblocking (STDOUT_FILENO, false);
185 for (;;)
186 {
187 ssize_t n;
188
189 /* Write buffer. */
190 while (p->size > 0)
191 {
192 n = write (p->out, p->buf + p->ofs, p->size);
193 if (n < 0)
194 fail_io ("write");
195 else if (n == 0)
196 fail_io ("zero-length write");
197 p->ofs += n;
198 p->size -= n;
199 }
200 p->ofs = 0;
201
202 p->size = n = read (p->in, p->buf, sizeof p->buf);
203 if (n <= 0)
204 return;
205 }
206 }
207
208 for (i = 0; i < 2; i++)
209 {
210 struct pipe *p = &pipes[i];
211 if (p->in != -1 && FD_ISSET (p->in, &read_fds))
212 {
213 ssize_t n = read (p->in, p->buf + p->ofs + p->size,
214 sizeof p->buf - p->ofs - p->size);
215 if (n > 0)
216 {
217 p->active = true;
218 p->size += n;
219 if (p->size == BUFSIZ && p->ofs != 0)
220 {
221 memmove (p->buf, p->buf + p->ofs, p->size);
222 p->ofs = 0;
223 }
224 }
225 else if (!handle_error (n, &p->in, p->in == pty, "read"))
226 return;
227 }
228 if (p->out != -1 && FD_ISSET (p->out, &write_fds))
229 {
230 ssize_t n = write (p->out, p->buf + p->ofs, p->size);
231 if (n > 0)
232 {
233 p->ofs += n;
234 p->size -= n;
235 if (p->size == 0)
236 p->ofs = 0;
237 }
238 else if (!handle_error (n, &p->out, p->out == pty, "write"))
239 return;
240 }
241 }
242 }
243}
244
245static int dead_child_fd;
246
247static void
248sigchld_handler (int signo __attribute__ ((unused)))
249{
250 if (write (dead_child_fd, "", 1) < 0)
251 _exit (1);
252}
253
254int
255main (int argc __attribute__ ((unused)), char *argv[])
256{
257 int master, slave;
258 char *name;
259 pid_t pid;
260 struct sigaction sa;
261 int pipe_fds[2];
262 struct itimerval zero_itimerval, old_itimerval;
263
264 if (argc < 2)
265 {
266 fprintf (stderr,
267 "usage: squish-pty COMMAND [ARG]...\n"
268 "Squishes both stdin and stdout into a single pseudoterminal,\n"
269 "which is passed as stdout to run the specified COMMAND.\n");
270 return EXIT_FAILURE;
271 }
272
273 /* Open master side of pty and get ready to open slave. */
274 master = open ("/dev/ptmx", O_RDWR | O_NOCTTY);
275 if (master < 0)
276 fail_io ("open \"/dev/ptmx\"");
277 if (grantpt (master) < 0)
278 fail_io ("grantpt");
279 if (unlockpt (master) < 0)
280 fail_io ("unlockpt");
281
282 /* Open slave side of pty. */
283 name = ptsname (master);
284 if (name == NULL)
285 fail_io ("ptsname");
286 slave = open (name, O_RDWR);
287 if (slave < 0)
288 fail_io ("open \"%s\"", name);
289
290 /* System V implementations need STREAMS configuration for the
291 slave. */
292 if (isastream (slave))
293 {
294 if (ioctl (slave, I_PUSH, "ptem") < 0
295 || ioctl (slave, I_PUSH, "ldterm") < 0)
296 fail_io ("ioctl");
297 }
298
299 /* Arrange to get notified when a child dies, by writing a byte
300 to a pipe fd. We really want to use pselect() and
301 sigprocmask(), but Solaris 2.7 doesn't have it. */
302 if (pipe (pipe_fds) < 0)
303 fail_io ("pipe");
304 dead_child_fd = pipe_fds[1];
305
306 memset (&sa, 0, sizeof sa);
307 sa.sa_handler = sigchld_handler;
308 sigemptyset (&sa.sa_mask);
309 sa.sa_flags = SA_RESTART;
310 if (sigaction (SIGCHLD, &sa, NULL) < 0)
311 fail_io ("sigaction");
312
313 /* Save the virtual interval timer, which might have been set
314 by the process that ran us. It really should be applied to
315 our child process. */
316 memset (&zero_itimerval, 0, sizeof zero_itimerval);
317 if (setitimer (ITIMER_VIRTUAL, &zero_itimerval, &old_itimerval) < 0)
318 fail_io ("setitimer");
319
320 pid = fork ();
321 if (pid < 0)
322 fail_io ("fork");
323 else if (pid != 0)
324 {
325 /* Running in parent process. */
326 int status;
327 close (slave);
328 relay (master, pipe_fds[0]);
329
330 /* If the subprocess has died, die in the same fashion.
331 In particular, dying from SIGVTALRM tells the pintos
332 script that we ran out of CPU time. */
333 if (waitpid (pid, &status, WNOHANG) > 0)
334 {
335 if (WIFEXITED (status))
336 return WEXITSTATUS (status);
337 else if (WIFSIGNALED (status))
338 raise (WTERMSIG (status));
339 }
340 return 0;
341 }
342 else
343 {
344 /* Running in child process. */
345 if (setitimer (ITIMER_VIRTUAL, &old_itimerval, NULL) < 0)
346 fail_io ("setitimer");
347 if (dup2 (slave, STDOUT_FILENO) < 0)
348 fail_io ("dup2");
349 if (close (pipe_fds[0]) < 0 || close (pipe_fds[1]) < 0
350 || close (slave) < 0 || close (master) < 0)
351 fail_io ("close");
352 execvp (argv[1], argv + 1);
353 fail_io ("exec");
354 }
355}
diff --git a/pintos-progos/utils/squish-unix.c b/pintos-progos/utils/squish-unix.c
new file mode 100644
index 0000000..805205b
--- /dev/null
+++ b/pintos-progos/utils/squish-unix.c
@@ -0,0 +1,338 @@
1#define _GNU_SOURCE 1
2#include <errno.h>
3#include <fcntl.h>
4#include <signal.h>
5#include <stdarg.h>
6#include <stdbool.h>
7#include <stddef.h>
8#include <stdio.h>
9#include <stdlib.h>
10#include <string.h>
11#include <stropts.h>
12#include <sys/ioctl.h>
13#include <sys/stat.h>
14#include <sys/time.h>
15#include <sys/types.h>
16#include <sys/wait.h>
17#include <sys/socket.h>
18#include <sys/un.h>
19#include <termios.h>
20#include <unistd.h>
21
22static void
23fail_io (const char *msg, ...)
24 __attribute__ ((noreturn))
25 __attribute__ ((format (printf, 1, 2)));
26
27/* Prints MSG, formatting as with printf(),
28 plus an error message based on errno,
29 and exits. */
30static void
31fail_io (const char *msg, ...)
32{
33 va_list args;
34
35 va_start (args, msg);
36 vfprintf (stderr, msg, args);
37 va_end (args);
38
39 if (errno != 0)
40 fprintf (stderr, ": %s", strerror (errno));
41 putc ('\n', stderr);
42 exit (EXIT_FAILURE);
43}
44
45/* If FD is a terminal, configures it for noncanonical input mode
46 with VMIN and VTIME set as indicated.
47 If FD is not a terminal, has no effect. */
48static void
49make_noncanon (int fd, int vmin, int vtime)
50{
51 if (isatty (fd))
52 {
53 struct termios termios;
54 if (tcgetattr (fd, &termios) < 0)
55 fail_io ("tcgetattr");
56 termios.c_lflag &= ~(ICANON | ECHO);
57 termios.c_cc[VMIN] = vmin;
58 termios.c_cc[VTIME] = vtime;
59 if (tcsetattr (fd, TCSANOW, &termios) < 0)
60 fail_io ("tcsetattr");
61 }
62}
63
64/* Make FD non-blocking if NONBLOCKING is true,
65 or blocking if NONBLOCKING is false. */
66static void
67make_nonblocking (int fd, bool nonblocking)
68{
69 int flags = fcntl (fd, F_GETFL);
70 if (flags < 0)
71 fail_io ("fcntl");
72 if (nonblocking)
73 flags |= O_NONBLOCK;
74 else
75 flags &= ~O_NONBLOCK;
76 if (fcntl (fd, F_SETFL, flags) < 0)
77 fail_io ("fcntl");
78}
79
80/* Handle a read or write on *FD, which is the socket if
81 FD_IS_SOCK is true, that returned end-of-file or error
82 indication RETVAL. The system call is named CALL, for use in
83 error messages. Returns true if processing may continue,
84 false if we're all done. */
85static bool
86handle_error (ssize_t retval, int *fd, bool fd_is_sock, const char *call)
87{
88 if (retval == 0)
89 {
90 if (fd_is_sock)
91 return false;
92 else
93 {
94 *fd = -1;
95 return true;
96 }
97 }
98 else
99 fail_io (call);
100}
101
102/* Copies data from stdin to SOCK and from SOCK to stdout until no
103 more data can be read or written. */
104static void
105relay (int sock)
106{
107 struct pipe
108 {
109 int in, out;
110 char buf[BUFSIZ];
111 size_t size, ofs;
112 bool active;
113 };
114 struct pipe pipes[2];
115
116 /* In case stdin is a file, go back to the beginning.
117 This allows replaying the input on reset. */
118 lseek (STDIN_FILENO, 0, SEEK_SET);
119
120 /* Make SOCK, stdin, and stdout non-blocking. */
121 make_nonblocking (sock, true);
122 make_nonblocking (STDIN_FILENO, true);
123 make_nonblocking (STDOUT_FILENO, true);
124
125 /* Configure noncanonical mode on stdin to avoid waiting for
126 end-of-line. */
127 make_noncanon (STDIN_FILENO, 1, 0);
128
129 memset (pipes, 0, sizeof pipes);
130 pipes[0].in = STDIN_FILENO;
131 pipes[0].out = sock;
132 pipes[1].in = sock;
133 pipes[1].out = STDOUT_FILENO;
134
135 while (pipes[0].in != -1 || pipes[1].in != -1
136 || (pipes[1].size && pipes[1].out != -1))
137 {
138 fd_set read_fds, write_fds;
139 sigset_t empty_set;
140 int retval;
141 int i;
142
143 FD_ZERO (&read_fds);
144 FD_ZERO (&write_fds);
145 for (i = 0; i < 2; i++)
146 {
147 struct pipe *p = &pipes[i];
148
149 /* Don't do anything with the stdin->sock pipe until we
150 have some data for the sock->stdout pipe. If we get
151 too eager, vmplayer will throw away our input. */
152 if (i == 0 && !pipes[1].active)
153 continue;
154
155 if (p->in != -1 && p->size + p->ofs < sizeof p->buf)
156 FD_SET (p->in, &read_fds);
157 if (p->out != -1 && p->size > 0)
158 FD_SET (p->out, &write_fds);
159 }
160 sigemptyset (&empty_set);
161 retval = pselect (FD_SETSIZE, &read_fds, &write_fds, NULL, NULL,
162 &empty_set);
163 if (retval < 0)
164 {
165 if (errno == EINTR)
166 {
167 /* Child died. Do final relaying. */
168 struct pipe *p = &pipes[1];
169 if (p->out == -1)
170 exit (0);
171 make_nonblocking (STDOUT_FILENO, false);
172 for (;;)
173 {
174 ssize_t n;
175
176 /* Write buffer. */
177 while (p->size > 0)
178 {
179 n = write (p->out, p->buf + p->ofs, p->size);
180 if (n < 0)
181 fail_io ("write");
182 else if (n == 0)
183 fail_io ("zero-length write");
184 p->ofs += n;
185 p->size -= n;
186 }
187 p->ofs = 0;
188
189 p->size = n = read (p->in, p->buf, sizeof p->buf);
190 if (n <= 0)
191 exit (0);
192 }
193 }
194 fail_io ("select");
195 }
196
197 for (i = 0; i < 2; i++)
198 {
199 struct pipe *p = &pipes[i];
200 if (p->in != -1 && FD_ISSET (p->in, &read_fds))
201 {
202 ssize_t n = read (p->in, p->buf + p->ofs + p->size,
203 sizeof p->buf - p->ofs - p->size);
204 if (n > 0)
205 {
206 p->active = true;
207 p->size += n;
208 if (p->size == BUFSIZ && p->ofs != 0)
209 {
210 memmove (p->buf, p->buf + p->ofs, p->size);
211 p->ofs = 0;
212 }
213 }
214 else if (!handle_error (n, &p->in, p->in == sock, "read"))
215 return;
216 }
217 if (p->out != -1 && FD_ISSET (p->out, &write_fds))
218 {
219 ssize_t n = write (p->out, p->buf + p->ofs, p->size);
220 if (n > 0)
221 {
222 p->ofs += n;
223 p->size -= n;
224 if (p->size == 0)
225 p->ofs = 0;
226 }
227 else if (!handle_error (n, &p->out, p->out == sock, "write"))
228 return;
229 }
230 }
231 }
232}
233
234static void
235sigchld_handler (int signo __attribute__ ((unused)))
236{
237 /* Nothing to do. */
238}
239
240int
241main (int argc __attribute__ ((unused)), char *argv[])
242{
243 pid_t pid;
244 struct itimerval zero_itimerval;
245 struct sockaddr_un sun;
246 sigset_t sigchld_set;
247 int sock;
248
249 if (argc < 3)
250 {
251 fprintf (stderr,
252 "usage: squish-unix SOCKET COMMAND [ARG]...\n"
253 "Squishes both stdin and stdout into a single Unix domain\n"
254 "socket named SOCKET, and runs COMMAND as a subprocess.\n");
255 return EXIT_FAILURE;
256 }
257
258 /* Create socket. */
259 sock = socket (PF_LOCAL, SOCK_STREAM, 0);
260 if (sock < 0)
261 fail_io ("socket");
262
263 /* Configure socket. */
264 sun.sun_family = AF_LOCAL;
265 strncpy (sun.sun_path, argv[1], sizeof sun.sun_path);
266 sun.sun_path[sizeof sun.sun_path - 1] = '\0';
267 if (unlink (sun.sun_path) < 0 && errno != ENOENT)
268 fail_io ("unlink");
269 if (bind (sock, (struct sockaddr *) &sun,
270 (offsetof (struct sockaddr_un, sun_path)
271 + strlen (sun.sun_path) + 1)) < 0)
272 fail_io ("bind");
273
274 /* Listen on socket. */
275 if (listen (sock, 1) < 0)
276 fail_io ("listen");
277
278 /* Block SIGCHLD and set up a handler for it. */
279 sigemptyset (&sigchld_set);
280 sigaddset (&sigchld_set, SIGCHLD);
281 if (sigprocmask (SIG_BLOCK, &sigchld_set, NULL) < 0)
282 fail_io ("sigprocmask");
283 if (signal (SIGCHLD, sigchld_handler) == SIG_ERR)
284 fail_io ("signal");
285
286 /* Save the virtual interval timer, which might have been set
287 by the process that ran us. It really should be applied to
288 our child process. */
289 memset (&zero_itimerval, 0, sizeof zero_itimerval);
290 if (setitimer (ITIMER_VIRTUAL, &zero_itimerval, NULL) < 0)
291 fail_io ("setitimer");
292
293 pid = fork ();
294 if (pid < 0)
295 fail_io ("fork");
296 else if (pid != 0)
297 {
298 /* Running in parent process. */
299 make_nonblocking (sock, true);
300 for (;;)
301 {
302 fd_set read_fds;
303 sigset_t empty_set;
304 int retval;
305 int conn;
306
307 /* Wait for connection. */
308 FD_ZERO (&read_fds);
309 FD_SET (sock, &read_fds);
310 sigemptyset (&empty_set);
311 retval = pselect (sock + 1, &read_fds, NULL, NULL, NULL, &empty_set);
312 if (retval < 0)
313 {
314 if (errno == EINTR)
315 break;
316 fail_io ("select");
317 }
318
319 /* Accept connection. */
320 conn = accept (sock, NULL, NULL);
321 if (conn < 0)
322 fail_io ("accept");
323
324 /* Relay connection. */
325 relay (conn);
326 close (conn);
327 }
328 return 0;
329 }
330 else
331 {
332 /* Running in child process. */
333 if (close (sock) < 0)
334 fail_io ("close");
335 execvp (argv[2], argv + 2);
336 fail_io ("exec");
337 }
338}
diff --git a/pintos-progos/vm/.gitignore b/pintos-progos/vm/.gitignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/pintos-progos/vm/.gitignore
@@ -0,0 +1,3 @@
1build
2bochsrc.txt
3bochsout.txt
diff --git a/pintos-progos/vm/Make.vars b/pintos-progos/vm/Make.vars
new file mode 100644
index 0000000..e3c33a7
--- /dev/null
+++ b/pintos-progos/vm/Make.vars
@@ -0,0 +1,7 @@
1# -*- makefile -*-
2
3kernel.bin: DEFINES = -DUSERPROG -DFILESYS -DVM
4KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys vm
5TEST_SUBDIRS = tests/userprog tests/vm tests/filesys/base
6GRADING_FILE = $(SRCDIR)/tests/vm/Grading
7SIMULATOR = --qemu
diff --git a/pintos-progos/vm/Makefile b/pintos-progos/vm/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/pintos-progos/vm/Makefile
@@ -0,0 +1 @@
include ../Makefile.kernel