From 970586f4374cdfc766c2e4c934b2fc164569f974 Mon Sep 17 00:00:00 2001
From: Kernel_Innovators <t202414423994274@eduxiji.net>
Date: Wed, 25 Dec 2024 23:25:39 +0800
Subject: [PATCH] all_finishted

---
 AUTHORS                                       |   43 +
 LICENSE                                       |   56 +
 src/.gitignore                                |    4 +
 src/LICENSE                                   |   95 +
 src/Make.config                               |   56 +
 src/Makefile                                  |   29 +
 src/Makefile.build                            |  112 ++
 src/Makefile.kernel                           |   20 +
 src/Makefile.userprog                         |   52 +
 src/devices/block.c                           |  223 +++
 src/devices/block.h                           |   74 +
 src/devices/ide.c                             |  527 +++++
 src/devices/ide.h                             |    6 +
 src/devices/input.c                           |   52 +
 src/devices/input.h                           |   12 +
 src/devices/intq.c                            |  114 ++
 src/devices/intq.h                            |   43 +
 src/devices/kbd.c                             |  213 ++
 src/devices/kbd.h                             |    9 +
 src/devices/partition.c                       |  324 ++++
 src/devices/partition.h                       |    8 +
 src/devices/pit.c                             |   83 +
 src/devices/pit.h                             |    8 +
 src/devices/rtc.c                             |  113 ++
 src/devices/rtc.h                             |    8 +
 src/devices/serial.c                          |  228 +++
 src/devices/serial.h                          |   11 +
 src/devices/shutdown.c                        |  141 ++
 src/devices/shutdown.h                        |   19 +
 src/devices/speaker.c                         |   68 +
 src/devices/speaker.h                         |    8 +
 src/devices/timer.c                           |  246 +++
 src/devices/timer.h                           |   29 +
 src/devices/vga.c                             |  172 ++
 src/devices/vga.h                             |    6 +
 src/examples/.gitignore                       |   19 +
 src/examples/Makefile                         |   33 +
 src/examples/bubsort.c                        |   38 +
 src/examples/cat.c                            |   34 +
 src/examples/cmp.c                            |   68 +
 src/examples/cp.c                             |   55 +
 src/examples/echo.c                           |   14 +
 src/examples/halt.c                           |   14 +
 src/examples/hex-dump.c                       |   35 +
 src/examples/lib/.gitignore                   |    1 +
 src/examples/lib/user/.dummy                  |    0
 src/examples/lib/user/.gitignore              |    1 +
 src/examples/lineup.c                         |   46 +
 src/examples/ls.c                             |   90 +
 src/examples/matmult.c                        |   57 +
 src/examples/mcat.c                           |   45 +
 src/examples/mcp.c                            |   68 +
 src/examples/mkdir.c                          |   24 +
 src/examples/pwd.c                            |  152 ++
 src/examples/recursor.c                       |   34 +
 src/examples/rm.c                             |   21 +
 src/examples/shell.c                          |  104 +
 src/filesys/.gitignore                        |    3 +
 src/filesys/Make.vars                         |   13 +
 src/filesys/Makefile                          |    1 +
 src/filesys/cache.c                           |  149 ++
 src/filesys/cache.h                           |   15 +
 src/filesys/directory.c                       |  440 +++++
 src/filesys/directory.h                       |   52 +
 src/filesys/file.c                            |  207 ++
 src/filesys/file.h                            |   37 +
 src/filesys/filesys.c                         |  331 ++++
 src/filesys/filesys.h                         |   27 +
 src/filesys/free-map.c                        |   85 +
 src/filesys/free-map.h                        |   17 +
 src/filesys/fsutil.c                          |  223 +++
 src/filesys/fsutil.h                          |   10 +
 src/filesys/inode.c                           |  515 +++++
 src/filesys/inode.h                           |   28 +
 src/filesys/off_t.h                           |   15 +
 src/lib/arithmetic.c                          |  189 ++
 src/lib/ctype.h                               |   28 +
 src/lib/debug.c                               |   32 +
 src/lib/debug.h                               |   39 +
 src/lib/inttypes.h                            |   48 +
 src/lib/kernel/bitmap.c                       |  371 ++++
 src/lib/kernel/bitmap.h                       |   51 +
 src/lib/kernel/console.c                      |  191 ++
 src/lib/kernel/console.h                      |    8 +
 src/lib/kernel/debug.c                        |  123 ++
 src/lib/kernel/hash.c                         |  430 +++++
 src/lib/kernel/hash.h                         |  103 +
 src/lib/kernel/list.c                         |  524 +++++
 src/lib/kernel/list.h                         |  181 ++
 src/lib/kernel/stdio.h                        |    6 +
 src/lib/limits.h                              |   34 +
 src/lib/packed.h                              |   10 +
 src/lib/random.c                              |   86 +
 src/lib/random.h                              |   10 +
 src/lib/round.h                               |   18 +
 src/lib/stdarg.h                              |   14 +
 src/lib/stdbool.h                             |    9 +
 src/lib/stddef.h                              |   12 +
 src/lib/stdint.h                              |   51 +
 src/lib/stdio.c                               |  655 +++++++
 src/lib/stdio.h                               |   40 +
 src/lib/stdlib.c                              |  208 ++
 src/lib/stdlib.h                              |   22 +
 src/lib/string.c                              |  375 ++++
 src/lib/string.h                              |   35 +
 src/lib/syscall-nr.h                          |   34 +
 src/lib/user/console.c                        |   94 +
 src/lib/user/debug.c                          |   25 +
 src/lib/user/entry.c                          |   10 +
 src/lib/user/stdio.h                          |    7 +
 src/lib/user/syscall.c                        |  184 ++
 src/lib/user/syscall.h                        |   48 +
 src/lib/user/user.lds                         |   57 +
 src/lib/ustar.c                               |  228 +++
 src/lib/ustar.h                               |   29 +
 src/misc/bochs-2.2.6-big-endian.patch         |   63 +
 src/misc/bochs-2.2.6-build.sh                 |   41 +
 src/misc/bochs-2.2.6-gdbstub-ENN.patch        |   29 +
 src/misc/bochs-2.2.6-jitter.patch             |   61 +
 src/misc/bochs-2.2.6-ms-extensions.patch      |   14 +
 src/misc/bochs-2.2.6-namespace.patch          |   10 +
 src/misc/bochs-2.2.6-page-fault-segv.patch    |   79 +
 src/misc/bochs-2.2.6-paranoia.patch           |   19 +
 src/misc/bochs-2.2.6-solaris-link.patch       |   11 +
 src/misc/bochs-2.2.6-solaris-tty.patch        |   31 +
 src/misc/bochs-2.2.6-triple-fault.patch       |   57 +
 src/misc/bochs-2.6.11-banner-stderr.patch     |   26 +
 src/misc/bochs-2.6.11-build.sh                |   49 +
 src/misc/bochs-2.6.11-jitter-plus-segv.patch  |  135 ++
 src/misc/bochs-2.6.11-link-tinfo.patch        |   22 +
 src/misc/bochs-2.6.2-banner-stderr.patch      |   32 +
 src/misc/bochs-2.6.2-block-device-check.patch |   12 +
 src/misc/bochs-2.6.2-build.sh                 |   34 +
 src/misc/bochs-2.6.2-jitter-plus-segv.patch   |  135 ++
 src/misc/bochs-2.6.2-link-tinfo.patch         |   22 +
 src/misc/bochs-2.6.2-xrandr-pkgconfig.patch   |   14 +
 src/misc/gcc-3.3.6-cross-howto                |   39 +
 src/misc/gdb-macros                           |  140 ++
 src/tests/Algorithm/Diff.pm                   | 1713 +++++++++++++++++
 src/tests/Make.tests                          |   77 +
 src/tests/arc4.c                              |   53 +
 src/tests/arc4.h                              |   17 +
 src/tests/arc4.pm                             |   29 +
 src/tests/cksum.c                             |   92 +
 src/tests/cksum.h                             |    8 +
 src/tests/cksum.pm                            |   87 +
 src/tests/filesys/Grading.no-vm               |   18 +
 src/tests/filesys/Grading.with-vm             |   22 +
 src/tests/filesys/base/Make.tests             |   18 +
 src/tests/filesys/base/Rubric                 |   19 +
 src/tests/filesys/base/child-syn-read.c       |   43 +
 src/tests/filesys/base/child-syn-wrt.c        |   35 +
 src/tests/filesys/base/full.inc               |   20 +
 src/tests/filesys/base/lg-create.c            |    5 +
 src/tests/filesys/base/lg-create.ck           |   13 +
 src/tests/filesys/base/lg-full.c              |    6 +
 src/tests/filesys/base/lg-full.ck             |   16 +
 src/tests/filesys/base/lg-random.c            |    7 +
 src/tests/filesys/base/lg-random.ck           |   14 +
 src/tests/filesys/base/lg-seq-block.c         |    7 +
 src/tests/filesys/base/lg-seq-block.ck        |   16 +
 src/tests/filesys/base/lg-seq-random.c        |    6 +
 src/tests/filesys/base/lg-seq-random.ck       |   16 +
 src/tests/filesys/base/random.inc             |   59 +
 src/tests/filesys/base/seq-block.inc          |   20 +
 src/tests/filesys/base/seq-random.inc         |   22 +
 src/tests/filesys/base/sm-create.c            |    5 +
 src/tests/filesys/base/sm-create.ck           |   13 +
 src/tests/filesys/base/sm-full.c              |    6 +
 src/tests/filesys/base/sm-full.ck             |   16 +
 src/tests/filesys/base/sm-random.c            |    7 +
 src/tests/filesys/base/sm-random.ck           |   14 +
 src/tests/filesys/base/sm-seq-block.c         |    7 +
 src/tests/filesys/base/sm-seq-block.ck        |   16 +
 src/tests/filesys/base/sm-seq-random.c        |    6 +
 src/tests/filesys/base/sm-seq-random.ck       |   16 +
 src/tests/filesys/base/syn-read.c             |   31 +
 src/tests/filesys/base/syn-read.ck            |   33 +
 src/tests/filesys/base/syn-read.h             |    7 +
 src/tests/filesys/base/syn-remove.c           |   30 +
 src/tests/filesys/base/syn-remove.ck          |   16 +
 src/tests/filesys/base/syn-write.c            |   31 +
 src/tests/filesys/base/syn-write.ck           |   32 +
 src/tests/filesys/base/syn-write.h            |    9 +
 src/tests/filesys/create.inc                  |   15 +
 src/tests/filesys/extended/Make.tests         |   61 +
 .../filesys/extended/Rubric.functionality     |   26 +
 src/tests/filesys/extended/Rubric.persistence |   24 +
 src/tests/filesys/extended/Rubric.robustness  |    9 +
 src/tests/filesys/extended/child-syn-rw.c     |   52 +
 .../extended/dir-empty-name-persistence.ck    |    6 +
 src/tests/filesys/extended/dir-empty-name.c   |   12 +
 src/tests/filesys/extended/dir-empty-name.ck  |   10 +
 .../extended/dir-mk-tree-persistence.ck       |   16 +
 src/tests/filesys/extended/dir-mk-tree.c      |   12 +
 src/tests/filesys/extended/dir-mk-tree.ck     |   12 +
 .../filesys/extended/dir-mkdir-persistence.ck |    6 +
 src/tests/filesys/extended/dir-mkdir.c        |   15 +
 src/tests/filesys/extended/dir-mkdir.ck       |   13 +
 .../filesys/extended/dir-open-persistence.ck  |    6 +
 src/tests/filesys/extended/dir-open.c         |   21 +
 src/tests/filesys/extended/dir-open.ck        |   20 +
 .../extended/dir-over-file-persistence.ck     |    6 +
 src/tests/filesys/extended/dir-over-file.c    |   13 +
 src/tests/filesys/extended/dir-over-file.ck   |   11 +
 .../extended/dir-rm-cwd-persistence.ck        |    8 +
 src/tests/filesys/extended/dir-rm-cwd.c       |   75 +
 src/tests/filesys/extended/dir-rm-cwd.ck      |   51 +
 .../extended/dir-rm-parent-persistence.ck     |    6 +
 src/tests/filesys/extended/dir-rm-parent.c    |   16 +
 src/tests/filesys/extended/dir-rm-parent.ck   |   14 +
 .../extended/dir-rm-root-persistence.ck       |    6 +
 src/tests/filesys/extended/dir-rm-root.c      |   13 +
 src/tests/filesys/extended/dir-rm-root.ck     |   11 +
 .../extended/dir-rm-tree-persistence.ck       |    6 +
 src/tests/filesys/extended/dir-rm-tree.c      |   62 +
 src/tests/filesys/extended/dir-rm-tree.ck     |   14 +
 .../filesys/extended/dir-rmdir-persistence.ck |    6 +
 src/tests/filesys/extended/dir-rmdir.c        |   14 +
 src/tests/filesys/extended/dir-rmdir.ck       |   12 +
 .../extended/dir-under-file-persistence.ck    |    6 +
 src/tests/filesys/extended/dir-under-file.c   |   13 +
 src/tests/filesys/extended/dir-under-file.ck  |   11 +
 .../filesys/extended/dir-vine-persistence.ck  |   37 +
 src/tests/filesys/extended/dir-vine.c         |   85 +
 src/tests/filesys/extended/dir-vine.ck        |   11 +
 .../extended/grow-create-persistence.ck       |    6 +
 src/tests/filesys/extended/grow-create.c      |    4 +
 src/tests/filesys/extended/grow-create.ck     |   13 +
 .../extended/grow-dir-lg-persistence.ck       |    9 +
 src/tests/filesys/extended/grow-dir-lg.c      |    6 +
 src/tests/filesys/extended/grow-dir-lg.ck     |   61 +
 src/tests/filesys/extended/grow-dir.inc       |   41 +
 .../extended/grow-file-size-persistence.ck    |    7 +
 src/tests/filesys/extended/grow-file-size.c   |   33 +
 src/tests/filesys/extended/grow-file-size.ck  |   17 +
 .../extended/grow-root-lg-persistence.ck      |    9 +
 src/tests/filesys/extended/grow-root-lg.c     |    4 +
 src/tests/filesys/extended/grow-root-lg.ck    |   60 +
 .../extended/grow-root-sm-persistence.ck      |    9 +
 src/tests/filesys/extended/grow-root-sm.c     |    4 +
 src/tests/filesys/extended/grow-root-sm.ck    |   30 +
 .../extended/grow-seq-lg-persistence.ck       |    7 +
 src/tests/filesys/extended/grow-seq-lg.c      |    5 +
 src/tests/filesys/extended/grow-seq-lg.ck     |   17 +
 .../extended/grow-seq-sm-persistence.ck       |    7 +
 src/tests/filesys/extended/grow-seq-sm.c      |    5 +
 src/tests/filesys/extended/grow-seq-sm.ck     |   17 +
 src/tests/filesys/extended/grow-seq.inc       |   20 +
 .../extended/grow-sparse-persistence.ck       |    6 +
 src/tests/filesys/extended/grow-sparse.c      |   25 +
 src/tests/filesys/extended/grow-sparse.ck     |   17 +
 .../filesys/extended/grow-tell-persistence.ck |    7 +
 src/tests/filesys/extended/grow-tell.c        |   32 +
 src/tests/filesys/extended/grow-tell.ck       |   17 +
 .../extended/grow-two-files-persistence.ck    |    9 +
 src/tests/filesys/extended/grow-two-files.c   |   62 +
 src/tests/filesys/extended/grow-two-files.ck  |   23 +
 src/tests/filesys/extended/mk-tree.c          |   67 +
 src/tests/filesys/extended/mk-tree.h          |    6 +
 .../filesys/extended/syn-rw-persistence.ck    |    8 +
 src/tests/filesys/extended/syn-rw.c           |   35 +
 src/tests/filesys/extended/syn-rw.ck          |   20 +
 src/tests/filesys/extended/syn-rw.h           |    9 +
 src/tests/filesys/extended/tar.c              |  208 ++
 src/tests/filesys/seq-test.c                  |   37 +
 src/tests/filesys/seq-test.h                  |   11 +
 src/tests/internal/list.c                     |  174 ++
 src/tests/internal/stdio.c                    |  208 ++
 src/tests/internal/stdlib.c                   |  114 ++
 src/tests/lib.c                               |  196 ++
 src/tests/lib.h                               |   50 +
 src/tests/lib.pm                              |   19 +
 src/tests/main.c                              |   15 +
 src/tests/main.h                              |    6 +
 src/tests/make-grade                          |  152 ++
 src/tests/random.pm                           |   27 +
 src/tests/tests.pm                            |  625 ++++++
 src/tests/threads/Grading                     |    6 +
 src/tests/threads/Make.tests                  |   53 +
 src/tests/threads/Rubric.alarm                |    8 +
 src/tests/threads/Rubric.mlfqs                |   14 +
 src/tests/threads/Rubric.priority             |   15 +
 src/tests/threads/alarm-multiple.ck           |    4 +
 src/tests/threads/alarm-negative.c            |   15 +
 src/tests/threads/alarm-negative.ck           |   10 +
 src/tests/threads/alarm-priority.c            |   58 +
 src/tests/threads/alarm-priority.ck           |   19 +
 src/tests/threads/alarm-simultaneous.c        |   94 +
 src/tests/threads/alarm-simultaneous.ck       |   27 +
 src/tests/threads/alarm-single.ck             |    4 +
 src/tests/threads/alarm-wait.c                |  152 ++
 src/tests/threads/alarm-zero.c                |   15 +
 src/tests/threads/alarm-zero.ck               |   10 +
 src/tests/threads/alarm.pm                    |   32 +
 src/tests/threads/mlfqs-block.c               |   64 +
 src/tests/threads/mlfqs-block.ck              |   17 +
 src/tests/threads/mlfqs-fair-2.ck             |    7 +
 src/tests/threads/mlfqs-fair-20.ck            |    7 +
 src/tests/threads/mlfqs-fair.c                |  124 ++
 src/tests/threads/mlfqs-load-1.c              |   60 +
 src/tests/threads/mlfqs-load-1.ck             |   15 +
 src/tests/threads/mlfqs-load-60.c             |  155 ++
 src/tests/threads/mlfqs-load-60.ck            |   36 +
 src/tests/threads/mlfqs-load-avg.c            |  167 ++
 src/tests/threads/mlfqs-load-avg.ck           |   36 +
 src/tests/threads/mlfqs-nice-10.ck            |    7 +
 src/tests/threads/mlfqs-nice-2.ck             |    7 +
 src/tests/threads/mlfqs-recent-1.c            |  144 ++
 src/tests/threads/mlfqs-recent-1.ck           |   31 +
 src/tests/threads/mlfqs.pm                    |  146 ++
 src/tests/threads/priority-change.c           |   31 +
 src/tests/threads/priority-change.ck          |   14 +
 src/tests/threads/priority-condvar.c          |   53 +
 src/tests/threads/priority-condvar.ck         |   39 +
 src/tests/threads/priority-donate-chain.c     |  114 ++
 src/tests/threads/priority-donate-chain.ck    |   46 +
 src/tests/threads/priority-donate-lower.c     |   51 +
 src/tests/threads/priority-donate-lower.ck    |   16 +
 src/tests/threads/priority-donate-multiple.c  |   77 +
 src/tests/threads/priority-donate-multiple.ck |   19 +
 src/tests/threads/priority-donate-multiple2.c |   90 +
 .../threads/priority-donate-multiple2.ck      |   19 +
 src/tests/threads/priority-donate-nest.c      |   94 +
 src/tests/threads/priority-donate-nest.ck     |   19 +
 src/tests/threads/priority-donate-one.c       |   65 +
 src/tests/threads/priority-donate-one.ck      |   17 +
 src/tests/threads/priority-donate-sema.c      |   82 +
 src/tests/threads/priority-donate-sema.ck     |   16 +
 src/tests/threads/priority-fifo.c             |   99 +
 src/tests/threads/priority-fifo.ck            |   63 +
 src/tests/threads/priority-preempt.c          |   41 +
 src/tests/threads/priority-preempt.ck         |   16 +
 src/tests/threads/priority-sema.c             |   45 +
 src/tests/threads/priority-sema.ck            |   29 +
 src/tests/threads/tests.c                     |  102 +
 src/tests/threads/tests.h                     |   41 +
 src/tests/userprog/Grading                    |   11 +
 src/tests/userprog/Make.tests                 |  143 ++
 src/tests/userprog/Rubric.functionality       |   52 +
 src/tests/userprog/Rubric.robustness          |   48 +
 src/tests/userprog/args-dbl-space.ck          |   15 +
 src/tests/userprog/args-many.ck               |   35 +
 src/tests/userprog/args-multiple.ck           |   17 +
 src/tests/userprog/args-none.ck               |   13 +
 src/tests/userprog/args-single.ck             |   14 +
 src/tests/userprog/args.c                     |   25 +
 src/tests/userprog/bad-jump.c                 |   16 +
 src/tests/userprog/bad-jump.ck                |    9 +
 src/tests/userprog/bad-jump2.c                |   13 +
 src/tests/userprog/bad-jump2.ck               |    9 +
 src/tests/userprog/bad-read.c                 |   13 +
 src/tests/userprog/bad-read.ck                |    9 +
 src/tests/userprog/bad-read2.c                |   13 +
 src/tests/userprog/bad-read2.ck               |    9 +
 src/tests/userprog/bad-write.c                |   12 +
 src/tests/userprog/bad-write.ck               |    9 +
 src/tests/userprog/bad-write2.c               |   12 +
 src/tests/userprog/bad-write2.ck              |    9 +
 src/tests/userprog/boundary.c                 |   47 +
 src/tests/userprog/boundary.h                 |    8 +
 src/tests/userprog/child-bad.c                |   14 +
 src/tests/userprog/child-close.c              |   28 +
 src/tests/userprog/child-rox.c                |   55 +
 src/tests/userprog/child-simple.c             |   15 +
 src/tests/userprog/close-bad-fd.c             |   11 +
 src/tests/userprog/close-bad-fd.ck            |   13 +
 src/tests/userprog/close-normal.c             |   14 +
 src/tests/userprog/close-normal.ck            |   12 +
 src/tests/userprog/close-stdin.c              |   11 +
 src/tests/userprog/close-stdin.ck             |   13 +
 src/tests/userprog/close-stdout.c             |   11 +
 src/tests/userprog/close-stdout.ck            |   13 +
 src/tests/userprog/close-twice.c              |   18 +
 src/tests/userprog/close-twice.ck             |   19 +
 src/tests/userprog/create-bad-ptr.c           |   12 +
 src/tests/userprog/create-bad-ptr.ck          |    9 +
 src/tests/userprog/create-bound.c             |   14 +
 src/tests/userprog/create-bound.ck            |   11 +
 src/tests/userprog/create-empty.c             |   10 +
 src/tests/userprog/create-empty.ck            |   14 +
 src/tests/userprog/create-exists.c            |   16 +
 src/tests/userprog/create-exists.ck           |   15 +
 src/tests/userprog/create-long.c              |   17 +
 src/tests/userprog/create-long.ck             |   11 +
 src/tests/userprog/create-normal.c            |   10 +
 src/tests/userprog/create-normal.ck           |   11 +
 src/tests/userprog/create-null.c              |   11 +
 src/tests/userprog/create-null.ck             |    9 +
 src/tests/userprog/exec-arg.c                 |   10 +
 src/tests/userprog/exec-arg.ck                |   17 +
 src/tests/userprog/exec-bad-ptr.c             |   11 +
 src/tests/userprog/exec-bad-ptr.ck            |   13 +
 src/tests/userprog/exec-bound-2.c             |   20 +
 src/tests/userprog/exec-bound-2.ck            |    9 +
 src/tests/userprog/exec-bound-3.c             |   28 +
 src/tests/userprog/exec-bound-3.ck            |    9 +
 src/tests/userprog/exec-bound.c               |   12 +
 src/tests/userprog/exec-bound.ck              |   18 +
 src/tests/userprog/exec-missing.c             |   12 +
 src/tests/userprog/exec-missing.ck            |   31 +
 src/tests/userprog/exec-multiple.c            |   14 +
 src/tests/userprog/exec-multiple.ck           |   18 +
 src/tests/userprog/exec-once.c                |   11 +
 src/tests/userprog/exec-once.ck               |   12 +
 src/tests/userprog/exit.c                     |   11 +
 src/tests/userprog/exit.ck                    |    9 +
 src/tests/userprog/halt.c                     |   11 +
 src/tests/userprog/halt.ck                    |   15 +
 src/tests/userprog/lib/.gitignore             |    1 +
 src/tests/userprog/lib/user/.dummy            |    0
 src/tests/userprog/lib/user/.gitignore        |    1 +
 src/tests/userprog/multi-child-fd.c           |   25 +
 src/tests/userprog/multi-child-fd.ck          |   25 +
 src/tests/userprog/multi-recurse.c            |   34 +
 src/tests/userprog/multi-recurse.ck           |   70 +
 src/tests/userprog/no-vm/Make.tests           |    8 +
 src/tests/userprog/no-vm/Rubric               |    3 +
 src/tests/userprog/no-vm/multi-oom.c          |  179 ++
 src/tests/userprog/no-vm/multi-oom.ck         |   10 +
 src/tests/userprog/null.ck                    |    8 +
 src/tests/userprog/open-bad-ptr.c             |   13 +
 src/tests/userprog/open-bad-ptr.ck            |   13 +
 src/tests/userprog/open-boundary.c            |   14 +
 src/tests/userprog/open-boundary.ck           |   11 +
 src/tests/userprog/open-empty.c               |   13 +
 src/tests/userprog/open-empty.ck              |   10 +
 src/tests/userprog/open-missing.c             |   13 +
 src/tests/userprog/open-missing.ck            |   10 +
 src/tests/userprog/open-normal.c              |   13 +
 src/tests/userprog/open-normal.ck             |   10 +
 src/tests/userprog/open-null.c                |   12 +
 src/tests/userprog/open-null.ck               |   13 +
 src/tests/userprog/open-twice.c               |   19 +
 src/tests/userprog/open-twice.ck              |   12 +
 src/tests/userprog/read-bad-fd.c              |   21 +
 src/tests/userprog/read-bad-fd.ck             |   13 +
 src/tests/userprog/read-bad-ptr.c             |   16 +
 src/tests/userprog/read-bad-ptr.ck            |   15 +
 src/tests/userprog/read-boundary.c            |   30 +
 src/tests/userprog/read-boundary.ck           |   11 +
 src/tests/userprog/read-normal.c              |   11 +
 src/tests/userprog/read-normal.ck             |   13 +
 src/tests/userprog/read-stdout.c              |   14 +
 src/tests/userprog/read-stdout.ck             |   13 +
 src/tests/userprog/read-zero.c                |   22 +
 src/tests/userprog/read-zero.ck               |   11 +
 src/tests/userprog/rox-child.c                |    5 +
 src/tests/userprog/rox-child.ck               |   20 +
 src/tests/userprog/rox-child.inc              |   33 +
 src/tests/userprog/rox-multichild.c           |    5 +
 src/tests/userprog/rox-multichild.ck          |   44 +
 src/tests/userprog/rox-simple.c               |   19 +
 src/tests/userprog/rox-simple.ck              |   13 +
 src/tests/userprog/sample.inc                 |    6 +
 src/tests/userprog/sample.txt                 |    4 +
 src/tests/userprog/sc-bad-arg.c               |   17 +
 src/tests/userprog/sc-bad-arg.ck              |    9 +
 src/tests/userprog/sc-bad-sp.c                |   20 +
 src/tests/userprog/sc-bad-sp.ck               |    9 +
 src/tests/userprog/sc-boundary-2.c            |   22 +
 src/tests/userprog/sc-boundary-2.ck           |    9 +
 src/tests/userprog/sc-boundary-3.c            |   20 +
 src/tests/userprog/sc-boundary-3.ck           |    9 +
 src/tests/userprog/sc-boundary.c              |   22 +
 src/tests/userprog/sc-boundary.ck             |    9 +
 src/tests/userprog/wait-bad-pid.c             |   11 +
 src/tests/userprog/wait-bad-pid.ck            |   13 +
 src/tests/userprog/wait-killed.c              |   11 +
 src/tests/userprog/wait-killed.ck             |   13 +
 src/tests/userprog/wait-simple.c              |   11 +
 src/tests/userprog/wait-simple.ck             |   13 +
 src/tests/userprog/wait-twice.c               |   15 +
 src/tests/userprog/wait-twice.ck              |   14 +
 src/tests/userprog/write-bad-fd.c             |   20 +
 src/tests/userprog/write-bad-fd.ck            |   13 +
 src/tests/userprog/write-bad-ptr.c            |   16 +
 src/tests/userprog/write-bad-ptr.ck           |   15 +
 src/tests/userprog/write-boundary.c           |   25 +
 src/tests/userprog/write-boundary.ck          |   11 +
 src/tests/userprog/write-normal.c             |   20 +
 src/tests/userprog/write-normal.ck            |   12 +
 src/tests/userprog/write-stdin.c              |   14 +
 src/tests/userprog/write-stdin.ck             |   13 +
 src/tests/userprog/write-zero.c               |   20 +
 src/tests/userprog/write-zero.ck              |   11 +
 src/tests/vm/Grading                          |   12 +
 src/tests/vm/Make.tests                       |  103 +
 src/tests/vm/Rubric.functionality             |   30 +
 src/tests/vm/Rubric.robustness                |   21 +
 src/tests/vm/child-inherit.c                  |   16 +
 src/tests/vm/child-linear.c                   |   36 +
 src/tests/vm/child-mm-wrt.c                   |   24 +
 src/tests/vm/child-qsort-mm.c                 |   24 +
 src/tests/vm/child-qsort.c                    |   31 +
 src/tests/vm/child-sort.c                     |   41 +
 src/tests/vm/mmap-bad-fd.c                    |   15 +
 src/tests/vm/mmap-bad-fd.ck                   |   15 +
 src/tests/vm/mmap-clean.c                     |   53 +
 src/tests/vm/mmap-clean.ck                    |   16 +
 src/tests/vm/mmap-close.c                     |   27 +
 src/tests/vm/mmap-close.ck                    |   11 +
 src/tests/vm/mmap-exit.c                      |   22 +
 src/tests/vm/mmap-exit.ck                     |   17 +
 src/tests/vm/mmap-inherit.c                   |   32 +
 src/tests/vm/mmap-inherit.ck                  |   16 +
 src/tests/vm/mmap-misalign.c                  |   16 +
 src/tests/vm/mmap-misalign.ck                 |   11 +
 src/tests/vm/mmap-null.c                      |   15 +
 src/tests/vm/mmap-null.ck                     |   11 +
 src/tests/vm/mmap-over-code.c                 |   19 +
 src/tests/vm/mmap-over-code.ck                |   11 +
 src/tests/vm/mmap-over-data.c                 |   21 +
 src/tests/vm/mmap-over-data.ck                |   11 +
 src/tests/vm/mmap-over-stk.c                  |   19 +
 src/tests/vm/mmap-over-stk.ck                 |   11 +
 src/tests/vm/mmap-overlap.c                   |   20 +
 src/tests/vm/mmap-overlap.ck                  |   13 +
 src/tests/vm/mmap-read.c                      |   32 +
 src/tests/vm/mmap-read.ck                     |   11 +
 src/tests/vm/mmap-remove.c                    |   43 +
 src/tests/vm/mmap-remove.ck                   |   14 +
 src/tests/vm/mmap-shuffle.c                   |   38 +
 src/tests/vm/mmap-shuffle.ck                  |   47 +
 src/tests/vm/mmap-twice.c                     |   28 +
 src/tests/vm/mmap-twice.ck                    |   15 +
 src/tests/vm/mmap-unmap.c                     |   23 +
 src/tests/vm/mmap-unmap.ck                    |    7 +
 src/tests/vm/mmap-write.c                     |   32 +
 src/tests/vm/mmap-write.ck                    |   13 +
 src/tests/vm/mmap-zero.c                      |   27 +
 src/tests/vm/mmap-zero.ck                     |   12 +
 src/tests/vm/page-linear.c                    |   44 +
 src/tests/vm/page-linear.ck                   |   14 +
 src/tests/vm/page-merge-mm.c                  |    8 +
 src/tests/vm/page-merge-mm.ck                 |   29 +
 src/tests/vm/page-merge-par.c                 |    8 +
 src/tests/vm/page-merge-par.ck                |   29 +
 src/tests/vm/page-merge-seq.c                 |  137 ++
 src/tests/vm/page-merge-seq.ck                |   29 +
 src/tests/vm/page-merge-stk.c                 |    8 +
 src/tests/vm/page-merge-stk.ck                |   29 +
 src/tests/vm/page-parallel.c                  |   21 +
 src/tests/vm/page-parallel.ck                 |   17 +
 src/tests/vm/page-shuffle.c                   |   30 +
 src/tests/vm/page-shuffle.ck                  |   44 +
 src/tests/vm/parallel-merge.c                 |  149 ++
 src/tests/vm/parallel-merge.h                 |    6 +
 src/tests/vm/process_death.pm                 |   22 +
 src/tests/vm/pt-bad-addr.c                    |   11 +
 src/tests/vm/pt-bad-addr.ck                   |    7 +
 src/tests/vm/pt-bad-read.c                    |   16 +
 src/tests/vm/pt-bad-read.ck                   |   10 +
 src/tests/vm/pt-big-stk-obj.c                 |   20 +
 src/tests/vm/pt-big-stk-obj.ck                |   10 +
 src/tests/vm/pt-grow-bad.c                    |   14 +
 src/tests/vm/pt-grow-bad.ck                   |    9 +
 src/tests/vm/pt-grow-pusha.c                  |   20 +
 src/tests/vm/pt-grow-pusha.ck                 |    9 +
 src/tests/vm/pt-grow-stack.c                  |   20 +
 src/tests/vm/pt-grow-stack.ck                 |   10 +
 src/tests/vm/pt-grow-stk-sc.c                 |   32 +
 src/tests/vm/pt-grow-stk-sc.ck                |   15 +
 src/tests/vm/pt-write-code-2.c                |   15 +
 src/tests/vm/pt-write-code.c                  |   12 +
 src/tests/vm/pt-write-code.ck                 |    7 +
 src/tests/vm/pt-write-code2.ck                |   10 +
 src/tests/vm/qsort.c                          |  136 ++
 src/tests/vm/qsort.h                          |    8 +
 src/tests/vm/sample.inc                       |   19 +
 src/tests/vm/sample.txt                       |   17 +
 src/threads/.gitignore                        |    3 +
 src/threads/Make.vars                         |    7 +
 src/threads/Makefile                          |    1 +
 src/threads/flags.h                           |    8 +
 src/threads/init.c                            |  446 +++++
 src/threads/init.h                            |   12 +
 src/threads/interrupt.c                       |  438 +++++
 src/threads/interrupt.h                       |   70 +
 src/threads/intr-stubs.S                      |  203 ++
 src/threads/intr-stubs.h                      |   19 +
 src/threads/io.h                              |  115 ++
 src/threads/kernel.lds.S                      |   33 +
 src/threads/loader.S                          |  263 +++
 src/threads/loader.h                          |   40 +
 src/threads/malloc.c                          |  294 +++
 src/threads/malloc.h                          |   13 +
 src/threads/palloc.c                          |  182 ++
 src/threads/palloc.h                          |   20 +
 src/threads/pte.h                             |  107 +
 src/threads/start.S                           |  204 ++
 src/threads/switch.S                          |   65 +
 src/threads/switch.h                          |   39 +
 src/threads/synch.c                           |  447 +++++
 src/threads/synch.h                           |   59 +
 src/threads/thread.c                          |  749 +++++++
 src/threads/thread.h                          |  251 +++
 src/threads/vaddr.h                           |   89 +
 src/userprog/.gitignore                       |    3 +
 src/userprog/Make.vars                        |    7 +
 src/userprog/Makefile                         |    1 +
 src/userprog/exception.c                      |  201 ++
 src/userprog/exception.h                      |   12 +
 src/userprog/gdt.c                            |  146 ++
 src/userprog/gdt.h                            |   15 +
 src/userprog/pagedir.c                        |  263 +++
 src/userprog/pagedir.h                        |   18 +
 src/userprog/process.c                        |  700 +++++++
 src/userprog/process.h                        |   15 +
 src/userprog/syscall.c                        | 1108 +++++++++++
 src/userprog/syscall.h                        |   39 +
 src/userprog/tss.c                            |  106 +
 src/userprog/tss.h                            |   11 +
 src/utils/.gitignore                          |    3 +
 src/utils/Makefile                            |   11 +
 src/utils/Pintos.pm                           |  491 +++++
 src/utils/backtrace                           |  106 +
 src/utils/pintos                              |  962 +++++++++
 src/utils/pintos-gdb                          |   20 +
 src/utils/pintos-mkdisk                       |  134 ++
 src/utils/pintos-set-cmdline                  |   42 +
 src/utils/setitimer-helper.c                  |   49 +
 src/utils/squish-pty.c                        |  351 ++++
 src/utils/squish-unix.c                       |  338 ++++
 src/vm/.gitignore                             |    3 +
 src/vm/Make.vars                              |    7 +
 src/vm/Makefile                               |    1 +
 src/vm/frame.c                                |  346 ++++
 src/vm/frame.h                                |   42 +
 src/vm/page.c                                 |  507 +++++
 src/vm/page.h                                 |   56 +
 src/vm/swap.c                                 |  142 ++
 src/vm/swap.h                                 |   29 +
 src/vm/vm.tmpl                                |  155 ++
 634 files changed, 36391 insertions(+)
 create mode 100644 AUTHORS
 create mode 100644 LICENSE
 create mode 100644 src/.gitignore
 create mode 100644 src/LICENSE
 create mode 100644 src/Make.config
 create mode 100644 src/Makefile
 create mode 100644 src/Makefile.build
 create mode 100644 src/Makefile.kernel
 create mode 100644 src/Makefile.userprog
 create mode 100644 src/devices/block.c
 create mode 100644 src/devices/block.h
 create mode 100644 src/devices/ide.c
 create mode 100644 src/devices/ide.h
 create mode 100644 src/devices/input.c
 create mode 100644 src/devices/input.h
 create mode 100644 src/devices/intq.c
 create mode 100644 src/devices/intq.h
 create mode 100644 src/devices/kbd.c
 create mode 100644 src/devices/kbd.h
 create mode 100644 src/devices/partition.c
 create mode 100644 src/devices/partition.h
 create mode 100644 src/devices/pit.c
 create mode 100644 src/devices/pit.h
 create mode 100644 src/devices/rtc.c
 create mode 100644 src/devices/rtc.h
 create mode 100644 src/devices/serial.c
 create mode 100644 src/devices/serial.h
 create mode 100644 src/devices/shutdown.c
 create mode 100644 src/devices/shutdown.h
 create mode 100644 src/devices/speaker.c
 create mode 100644 src/devices/speaker.h
 create mode 100644 src/devices/timer.c
 create mode 100644 src/devices/timer.h
 create mode 100644 src/devices/vga.c
 create mode 100644 src/devices/vga.h
 create mode 100644 src/examples/.gitignore
 create mode 100644 src/examples/Makefile
 create mode 100644 src/examples/bubsort.c
 create mode 100644 src/examples/cat.c
 create mode 100644 src/examples/cmp.c
 create mode 100644 src/examples/cp.c
 create mode 100644 src/examples/echo.c
 create mode 100644 src/examples/halt.c
 create mode 100644 src/examples/hex-dump.c
 create mode 100644 src/examples/lib/.gitignore
 create mode 100644 src/examples/lib/user/.dummy
 create mode 100644 src/examples/lib/user/.gitignore
 create mode 100644 src/examples/lineup.c
 create mode 100644 src/examples/ls.c
 create mode 100644 src/examples/matmult.c
 create mode 100644 src/examples/mcat.c
 create mode 100644 src/examples/mcp.c
 create mode 100644 src/examples/mkdir.c
 create mode 100644 src/examples/pwd.c
 create mode 100644 src/examples/recursor.c
 create mode 100644 src/examples/rm.c
 create mode 100644 src/examples/shell.c
 create mode 100644 src/filesys/.gitignore
 create mode 100644 src/filesys/Make.vars
 create mode 100644 src/filesys/Makefile
 create mode 100644 src/filesys/cache.c
 create mode 100644 src/filesys/cache.h
 create mode 100644 src/filesys/directory.c
 create mode 100644 src/filesys/directory.h
 create mode 100644 src/filesys/file.c
 create mode 100644 src/filesys/file.h
 create mode 100644 src/filesys/filesys.c
 create mode 100644 src/filesys/filesys.h
 create mode 100644 src/filesys/free-map.c
 create mode 100644 src/filesys/free-map.h
 create mode 100644 src/filesys/fsutil.c
 create mode 100644 src/filesys/fsutil.h
 create mode 100644 src/filesys/inode.c
 create mode 100644 src/filesys/inode.h
 create mode 100644 src/filesys/off_t.h
 create mode 100644 src/lib/arithmetic.c
 create mode 100644 src/lib/ctype.h
 create mode 100644 src/lib/debug.c
 create mode 100644 src/lib/debug.h
 create mode 100644 src/lib/inttypes.h
 create mode 100644 src/lib/kernel/bitmap.c
 create mode 100644 src/lib/kernel/bitmap.h
 create mode 100644 src/lib/kernel/console.c
 create mode 100644 src/lib/kernel/console.h
 create mode 100644 src/lib/kernel/debug.c
 create mode 100644 src/lib/kernel/hash.c
 create mode 100644 src/lib/kernel/hash.h
 create mode 100644 src/lib/kernel/list.c
 create mode 100644 src/lib/kernel/list.h
 create mode 100644 src/lib/kernel/stdio.h
 create mode 100644 src/lib/limits.h
 create mode 100644 src/lib/packed.h
 create mode 100644 src/lib/random.c
 create mode 100644 src/lib/random.h
 create mode 100644 src/lib/round.h
 create mode 100644 src/lib/stdarg.h
 create mode 100644 src/lib/stdbool.h
 create mode 100644 src/lib/stddef.h
 create mode 100644 src/lib/stdint.h
 create mode 100644 src/lib/stdio.c
 create mode 100644 src/lib/stdio.h
 create mode 100644 src/lib/stdlib.c
 create mode 100644 src/lib/stdlib.h
 create mode 100644 src/lib/string.c
 create mode 100644 src/lib/string.h
 create mode 100644 src/lib/syscall-nr.h
 create mode 100644 src/lib/user/console.c
 create mode 100644 src/lib/user/debug.c
 create mode 100644 src/lib/user/entry.c
 create mode 100644 src/lib/user/stdio.h
 create mode 100644 src/lib/user/syscall.c
 create mode 100644 src/lib/user/syscall.h
 create mode 100644 src/lib/user/user.lds
 create mode 100644 src/lib/ustar.c
 create mode 100644 src/lib/ustar.h
 create mode 100644 src/misc/bochs-2.2.6-big-endian.patch
 create mode 100644 src/misc/bochs-2.2.6-build.sh
 create mode 100644 src/misc/bochs-2.2.6-gdbstub-ENN.patch
 create mode 100644 src/misc/bochs-2.2.6-jitter.patch
 create mode 100644 src/misc/bochs-2.2.6-ms-extensions.patch
 create mode 100644 src/misc/bochs-2.2.6-namespace.patch
 create mode 100644 src/misc/bochs-2.2.6-page-fault-segv.patch
 create mode 100644 src/misc/bochs-2.2.6-paranoia.patch
 create mode 100644 src/misc/bochs-2.2.6-solaris-link.patch
 create mode 100644 src/misc/bochs-2.2.6-solaris-tty.patch
 create mode 100644 src/misc/bochs-2.2.6-triple-fault.patch
 create mode 100644 src/misc/bochs-2.6.11-banner-stderr.patch
 create mode 100644 src/misc/bochs-2.6.11-build.sh
 create mode 100644 src/misc/bochs-2.6.11-jitter-plus-segv.patch
 create mode 100644 src/misc/bochs-2.6.11-link-tinfo.patch
 create mode 100644 src/misc/bochs-2.6.2-banner-stderr.patch
 create mode 100644 src/misc/bochs-2.6.2-block-device-check.patch
 create mode 100644 src/misc/bochs-2.6.2-build.sh
 create mode 100644 src/misc/bochs-2.6.2-jitter-plus-segv.patch
 create mode 100644 src/misc/bochs-2.6.2-link-tinfo.patch
 create mode 100644 src/misc/bochs-2.6.2-xrandr-pkgconfig.patch
 create mode 100644 src/misc/gcc-3.3.6-cross-howto
 create mode 100644 src/misc/gdb-macros
 create mode 100644 src/tests/Algorithm/Diff.pm
 create mode 100644 src/tests/Make.tests
 create mode 100644 src/tests/arc4.c
 create mode 100644 src/tests/arc4.h
 create mode 100644 src/tests/arc4.pm
 create mode 100644 src/tests/cksum.c
 create mode 100644 src/tests/cksum.h
 create mode 100644 src/tests/cksum.pm
 create mode 100644 src/tests/filesys/Grading.no-vm
 create mode 100644 src/tests/filesys/Grading.with-vm
 create mode 100644 src/tests/filesys/base/Make.tests
 create mode 100644 src/tests/filesys/base/Rubric
 create mode 100644 src/tests/filesys/base/child-syn-read.c
 create mode 100644 src/tests/filesys/base/child-syn-wrt.c
 create mode 100644 src/tests/filesys/base/full.inc
 create mode 100644 src/tests/filesys/base/lg-create.c
 create mode 100644 src/tests/filesys/base/lg-create.ck
 create mode 100644 src/tests/filesys/base/lg-full.c
 create mode 100644 src/tests/filesys/base/lg-full.ck
 create mode 100644 src/tests/filesys/base/lg-random.c
 create mode 100644 src/tests/filesys/base/lg-random.ck
 create mode 100644 src/tests/filesys/base/lg-seq-block.c
 create mode 100644 src/tests/filesys/base/lg-seq-block.ck
 create mode 100644 src/tests/filesys/base/lg-seq-random.c
 create mode 100644 src/tests/filesys/base/lg-seq-random.ck
 create mode 100644 src/tests/filesys/base/random.inc
 create mode 100644 src/tests/filesys/base/seq-block.inc
 create mode 100644 src/tests/filesys/base/seq-random.inc
 create mode 100644 src/tests/filesys/base/sm-create.c
 create mode 100644 src/tests/filesys/base/sm-create.ck
 create mode 100644 src/tests/filesys/base/sm-full.c
 create mode 100644 src/tests/filesys/base/sm-full.ck
 create mode 100644 src/tests/filesys/base/sm-random.c
 create mode 100644 src/tests/filesys/base/sm-random.ck
 create mode 100644 src/tests/filesys/base/sm-seq-block.c
 create mode 100644 src/tests/filesys/base/sm-seq-block.ck
 create mode 100644 src/tests/filesys/base/sm-seq-random.c
 create mode 100644 src/tests/filesys/base/sm-seq-random.ck
 create mode 100644 src/tests/filesys/base/syn-read.c
 create mode 100644 src/tests/filesys/base/syn-read.ck
 create mode 100644 src/tests/filesys/base/syn-read.h
 create mode 100644 src/tests/filesys/base/syn-remove.c
 create mode 100644 src/tests/filesys/base/syn-remove.ck
 create mode 100644 src/tests/filesys/base/syn-write.c
 create mode 100644 src/tests/filesys/base/syn-write.ck
 create mode 100644 src/tests/filesys/base/syn-write.h
 create mode 100644 src/tests/filesys/create.inc
 create mode 100644 src/tests/filesys/extended/Make.tests
 create mode 100644 src/tests/filesys/extended/Rubric.functionality
 create mode 100644 src/tests/filesys/extended/Rubric.persistence
 create mode 100644 src/tests/filesys/extended/Rubric.robustness
 create mode 100644 src/tests/filesys/extended/child-syn-rw.c
 create mode 100644 src/tests/filesys/extended/dir-empty-name-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-empty-name.c
 create mode 100644 src/tests/filesys/extended/dir-empty-name.ck
 create mode 100644 src/tests/filesys/extended/dir-mk-tree-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-mk-tree.c
 create mode 100644 src/tests/filesys/extended/dir-mk-tree.ck
 create mode 100644 src/tests/filesys/extended/dir-mkdir-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-mkdir.c
 create mode 100644 src/tests/filesys/extended/dir-mkdir.ck
 create mode 100644 src/tests/filesys/extended/dir-open-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-open.c
 create mode 100644 src/tests/filesys/extended/dir-open.ck
 create mode 100644 src/tests/filesys/extended/dir-over-file-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-over-file.c
 create mode 100644 src/tests/filesys/extended/dir-over-file.ck
 create mode 100644 src/tests/filesys/extended/dir-rm-cwd-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-rm-cwd.c
 create mode 100644 src/tests/filesys/extended/dir-rm-cwd.ck
 create mode 100644 src/tests/filesys/extended/dir-rm-parent-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-rm-parent.c
 create mode 100644 src/tests/filesys/extended/dir-rm-parent.ck
 create mode 100644 src/tests/filesys/extended/dir-rm-root-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-rm-root.c
 create mode 100644 src/tests/filesys/extended/dir-rm-root.ck
 create mode 100644 src/tests/filesys/extended/dir-rm-tree-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-rm-tree.c
 create mode 100644 src/tests/filesys/extended/dir-rm-tree.ck
 create mode 100644 src/tests/filesys/extended/dir-rmdir-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-rmdir.c
 create mode 100644 src/tests/filesys/extended/dir-rmdir.ck
 create mode 100644 src/tests/filesys/extended/dir-under-file-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-under-file.c
 create mode 100644 src/tests/filesys/extended/dir-under-file.ck
 create mode 100644 src/tests/filesys/extended/dir-vine-persistence.ck
 create mode 100644 src/tests/filesys/extended/dir-vine.c
 create mode 100644 src/tests/filesys/extended/dir-vine.ck
 create mode 100644 src/tests/filesys/extended/grow-create-persistence.ck
 create mode 100644 src/tests/filesys/extended/grow-create.c
 create mode 100644 src/tests/filesys/extended/grow-create.ck
 create mode 100644 src/tests/filesys/extended/grow-dir-lg-persistence.ck
 create mode 100644 src/tests/filesys/extended/grow-dir-lg.c
 create mode 100644 src/tests/filesys/extended/grow-dir-lg.ck
 create mode 100644 src/tests/filesys/extended/grow-dir.inc
 create mode 100644 src/tests/filesys/extended/grow-file-size-persistence.ck
 create mode 100644 src/tests/filesys/extended/grow-file-size.c
 create mode 100644 src/tests/filesys/extended/grow-file-size.ck
 create mode 100644 src/tests/filesys/extended/grow-root-lg-persistence.ck
 create mode 100644 src/tests/filesys/extended/grow-root-lg.c
 create mode 100644 src/tests/filesys/extended/grow-root-lg.ck
 create mode 100644 src/tests/filesys/extended/grow-root-sm-persistence.ck
 create mode 100644 src/tests/filesys/extended/grow-root-sm.c
 create mode 100644 src/tests/filesys/extended/grow-root-sm.ck
 create mode 100644 src/tests/filesys/extended/grow-seq-lg-persistence.ck
 create mode 100644 src/tests/filesys/extended/grow-seq-lg.c
 create mode 100644 src/tests/filesys/extended/grow-seq-lg.ck
 create mode 100644 src/tests/filesys/extended/grow-seq-sm-persistence.ck
 create mode 100644 src/tests/filesys/extended/grow-seq-sm.c
 create mode 100644 src/tests/filesys/extended/grow-seq-sm.ck
 create mode 100644 src/tests/filesys/extended/grow-seq.inc
 create mode 100644 src/tests/filesys/extended/grow-sparse-persistence.ck
 create mode 100644 src/tests/filesys/extended/grow-sparse.c
 create mode 100644 src/tests/filesys/extended/grow-sparse.ck
 create mode 100644 src/tests/filesys/extended/grow-tell-persistence.ck
 create mode 100644 src/tests/filesys/extended/grow-tell.c
 create mode 100644 src/tests/filesys/extended/grow-tell.ck
 create mode 100644 src/tests/filesys/extended/grow-two-files-persistence.ck
 create mode 100644 src/tests/filesys/extended/grow-two-files.c
 create mode 100644 src/tests/filesys/extended/grow-two-files.ck
 create mode 100644 src/tests/filesys/extended/mk-tree.c
 create mode 100644 src/tests/filesys/extended/mk-tree.h
 create mode 100644 src/tests/filesys/extended/syn-rw-persistence.ck
 create mode 100644 src/tests/filesys/extended/syn-rw.c
 create mode 100644 src/tests/filesys/extended/syn-rw.ck
 create mode 100644 src/tests/filesys/extended/syn-rw.h
 create mode 100644 src/tests/filesys/extended/tar.c
 create mode 100644 src/tests/filesys/seq-test.c
 create mode 100644 src/tests/filesys/seq-test.h
 create mode 100644 src/tests/internal/list.c
 create mode 100644 src/tests/internal/stdio.c
 create mode 100644 src/tests/internal/stdlib.c
 create mode 100644 src/tests/lib.c
 create mode 100644 src/tests/lib.h
 create mode 100644 src/tests/lib.pm
 create mode 100644 src/tests/main.c
 create mode 100644 src/tests/main.h
 create mode 100644 src/tests/make-grade
 create mode 100644 src/tests/random.pm
 create mode 100644 src/tests/tests.pm
 create mode 100644 src/tests/threads/Grading
 create mode 100644 src/tests/threads/Make.tests
 create mode 100644 src/tests/threads/Rubric.alarm
 create mode 100644 src/tests/threads/Rubric.mlfqs
 create mode 100644 src/tests/threads/Rubric.priority
 create mode 100644 src/tests/threads/alarm-multiple.ck
 create mode 100644 src/tests/threads/alarm-negative.c
 create mode 100644 src/tests/threads/alarm-negative.ck
 create mode 100644 src/tests/threads/alarm-priority.c
 create mode 100644 src/tests/threads/alarm-priority.ck
 create mode 100644 src/tests/threads/alarm-simultaneous.c
 create mode 100644 src/tests/threads/alarm-simultaneous.ck
 create mode 100644 src/tests/threads/alarm-single.ck
 create mode 100644 src/tests/threads/alarm-wait.c
 create mode 100644 src/tests/threads/alarm-zero.c
 create mode 100644 src/tests/threads/alarm-zero.ck
 create mode 100644 src/tests/threads/alarm.pm
 create mode 100644 src/tests/threads/mlfqs-block.c
 create mode 100644 src/tests/threads/mlfqs-block.ck
 create mode 100644 src/tests/threads/mlfqs-fair-2.ck
 create mode 100644 src/tests/threads/mlfqs-fair-20.ck
 create mode 100644 src/tests/threads/mlfqs-fair.c
 create mode 100644 src/tests/threads/mlfqs-load-1.c
 create mode 100644 src/tests/threads/mlfqs-load-1.ck
 create mode 100644 src/tests/threads/mlfqs-load-60.c
 create mode 100644 src/tests/threads/mlfqs-load-60.ck
 create mode 100644 src/tests/threads/mlfqs-load-avg.c
 create mode 100644 src/tests/threads/mlfqs-load-avg.ck
 create mode 100644 src/tests/threads/mlfqs-nice-10.ck
 create mode 100644 src/tests/threads/mlfqs-nice-2.ck
 create mode 100644 src/tests/threads/mlfqs-recent-1.c
 create mode 100644 src/tests/threads/mlfqs-recent-1.ck
 create mode 100644 src/tests/threads/mlfqs.pm
 create mode 100644 src/tests/threads/priority-change.c
 create mode 100644 src/tests/threads/priority-change.ck
 create mode 100644 src/tests/threads/priority-condvar.c
 create mode 100644 src/tests/threads/priority-condvar.ck
 create mode 100644 src/tests/threads/priority-donate-chain.c
 create mode 100644 src/tests/threads/priority-donate-chain.ck
 create mode 100644 src/tests/threads/priority-donate-lower.c
 create mode 100644 src/tests/threads/priority-donate-lower.ck
 create mode 100644 src/tests/threads/priority-donate-multiple.c
 create mode 100644 src/tests/threads/priority-donate-multiple.ck
 create mode 100644 src/tests/threads/priority-donate-multiple2.c
 create mode 100644 src/tests/threads/priority-donate-multiple2.ck
 create mode 100644 src/tests/threads/priority-donate-nest.c
 create mode 100644 src/tests/threads/priority-donate-nest.ck
 create mode 100644 src/tests/threads/priority-donate-one.c
 create mode 100644 src/tests/threads/priority-donate-one.ck
 create mode 100644 src/tests/threads/priority-donate-sema.c
 create mode 100644 src/tests/threads/priority-donate-sema.ck
 create mode 100644 src/tests/threads/priority-fifo.c
 create mode 100644 src/tests/threads/priority-fifo.ck
 create mode 100644 src/tests/threads/priority-preempt.c
 create mode 100644 src/tests/threads/priority-preempt.ck
 create mode 100644 src/tests/threads/priority-sema.c
 create mode 100644 src/tests/threads/priority-sema.ck
 create mode 100644 src/tests/threads/tests.c
 create mode 100644 src/tests/threads/tests.h
 create mode 100644 src/tests/userprog/Grading
 create mode 100644 src/tests/userprog/Make.tests
 create mode 100644 src/tests/userprog/Rubric.functionality
 create mode 100644 src/tests/userprog/Rubric.robustness
 create mode 100644 src/tests/userprog/args-dbl-space.ck
 create mode 100644 src/tests/userprog/args-many.ck
 create mode 100644 src/tests/userprog/args-multiple.ck
 create mode 100644 src/tests/userprog/args-none.ck
 create mode 100644 src/tests/userprog/args-single.ck
 create mode 100644 src/tests/userprog/args.c
 create mode 100644 src/tests/userprog/bad-jump.c
 create mode 100644 src/tests/userprog/bad-jump.ck
 create mode 100644 src/tests/userprog/bad-jump2.c
 create mode 100644 src/tests/userprog/bad-jump2.ck
 create mode 100644 src/tests/userprog/bad-read.c
 create mode 100644 src/tests/userprog/bad-read.ck
 create mode 100644 src/tests/userprog/bad-read2.c
 create mode 100644 src/tests/userprog/bad-read2.ck
 create mode 100644 src/tests/userprog/bad-write.c
 create mode 100644 src/tests/userprog/bad-write.ck
 create mode 100644 src/tests/userprog/bad-write2.c
 create mode 100644 src/tests/userprog/bad-write2.ck
 create mode 100644 src/tests/userprog/boundary.c
 create mode 100644 src/tests/userprog/boundary.h
 create mode 100644 src/tests/userprog/child-bad.c
 create mode 100644 src/tests/userprog/child-close.c
 create mode 100644 src/tests/userprog/child-rox.c
 create mode 100644 src/tests/userprog/child-simple.c
 create mode 100644 src/tests/userprog/close-bad-fd.c
 create mode 100644 src/tests/userprog/close-bad-fd.ck
 create mode 100644 src/tests/userprog/close-normal.c
 create mode 100644 src/tests/userprog/close-normal.ck
 create mode 100644 src/tests/userprog/close-stdin.c
 create mode 100644 src/tests/userprog/close-stdin.ck
 create mode 100644 src/tests/userprog/close-stdout.c
 create mode 100644 src/tests/userprog/close-stdout.ck
 create mode 100644 src/tests/userprog/close-twice.c
 create mode 100644 src/tests/userprog/close-twice.ck
 create mode 100644 src/tests/userprog/create-bad-ptr.c
 create mode 100644 src/tests/userprog/create-bad-ptr.ck
 create mode 100644 src/tests/userprog/create-bound.c
 create mode 100644 src/tests/userprog/create-bound.ck
 create mode 100644 src/tests/userprog/create-empty.c
 create mode 100644 src/tests/userprog/create-empty.ck
 create mode 100644 src/tests/userprog/create-exists.c
 create mode 100644 src/tests/userprog/create-exists.ck
 create mode 100644 src/tests/userprog/create-long.c
 create mode 100644 src/tests/userprog/create-long.ck
 create mode 100644 src/tests/userprog/create-normal.c
 create mode 100644 src/tests/userprog/create-normal.ck
 create mode 100644 src/tests/userprog/create-null.c
 create mode 100644 src/tests/userprog/create-null.ck
 create mode 100644 src/tests/userprog/exec-arg.c
 create mode 100644 src/tests/userprog/exec-arg.ck
 create mode 100644 src/tests/userprog/exec-bad-ptr.c
 create mode 100644 src/tests/userprog/exec-bad-ptr.ck
 create mode 100644 src/tests/userprog/exec-bound-2.c
 create mode 100644 src/tests/userprog/exec-bound-2.ck
 create mode 100644 src/tests/userprog/exec-bound-3.c
 create mode 100644 src/tests/userprog/exec-bound-3.ck
 create mode 100644 src/tests/userprog/exec-bound.c
 create mode 100644 src/tests/userprog/exec-bound.ck
 create mode 100644 src/tests/userprog/exec-missing.c
 create mode 100644 src/tests/userprog/exec-missing.ck
 create mode 100644 src/tests/userprog/exec-multiple.c
 create mode 100644 src/tests/userprog/exec-multiple.ck
 create mode 100644 src/tests/userprog/exec-once.c
 create mode 100644 src/tests/userprog/exec-once.ck
 create mode 100644 src/tests/userprog/exit.c
 create mode 100644 src/tests/userprog/exit.ck
 create mode 100644 src/tests/userprog/halt.c
 create mode 100644 src/tests/userprog/halt.ck
 create mode 100644 src/tests/userprog/lib/.gitignore
 create mode 100644 src/tests/userprog/lib/user/.dummy
 create mode 100644 src/tests/userprog/lib/user/.gitignore
 create mode 100644 src/tests/userprog/multi-child-fd.c
 create mode 100644 src/tests/userprog/multi-child-fd.ck
 create mode 100644 src/tests/userprog/multi-recurse.c
 create mode 100644 src/tests/userprog/multi-recurse.ck
 create mode 100644 src/tests/userprog/no-vm/Make.tests
 create mode 100644 src/tests/userprog/no-vm/Rubric
 create mode 100644 src/tests/userprog/no-vm/multi-oom.c
 create mode 100644 src/tests/userprog/no-vm/multi-oom.ck
 create mode 100644 src/tests/userprog/null.ck
 create mode 100644 src/tests/userprog/open-bad-ptr.c
 create mode 100644 src/tests/userprog/open-bad-ptr.ck
 create mode 100644 src/tests/userprog/open-boundary.c
 create mode 100644 src/tests/userprog/open-boundary.ck
 create mode 100644 src/tests/userprog/open-empty.c
 create mode 100644 src/tests/userprog/open-empty.ck
 create mode 100644 src/tests/userprog/open-missing.c
 create mode 100644 src/tests/userprog/open-missing.ck
 create mode 100644 src/tests/userprog/open-normal.c
 create mode 100644 src/tests/userprog/open-normal.ck
 create mode 100644 src/tests/userprog/open-null.c
 create mode 100644 src/tests/userprog/open-null.ck
 create mode 100644 src/tests/userprog/open-twice.c
 create mode 100644 src/tests/userprog/open-twice.ck
 create mode 100644 src/tests/userprog/read-bad-fd.c
 create mode 100644 src/tests/userprog/read-bad-fd.ck
 create mode 100644 src/tests/userprog/read-bad-ptr.c
 create mode 100644 src/tests/userprog/read-bad-ptr.ck
 create mode 100644 src/tests/userprog/read-boundary.c
 create mode 100644 src/tests/userprog/read-boundary.ck
 create mode 100644 src/tests/userprog/read-normal.c
 create mode 100644 src/tests/userprog/read-normal.ck
 create mode 100644 src/tests/userprog/read-stdout.c
 create mode 100644 src/tests/userprog/read-stdout.ck
 create mode 100644 src/tests/userprog/read-zero.c
 create mode 100644 src/tests/userprog/read-zero.ck
 create mode 100644 src/tests/userprog/rox-child.c
 create mode 100644 src/tests/userprog/rox-child.ck
 create mode 100644 src/tests/userprog/rox-child.inc
 create mode 100644 src/tests/userprog/rox-multichild.c
 create mode 100644 src/tests/userprog/rox-multichild.ck
 create mode 100644 src/tests/userprog/rox-simple.c
 create mode 100644 src/tests/userprog/rox-simple.ck
 create mode 100644 src/tests/userprog/sample.inc
 create mode 100644 src/tests/userprog/sample.txt
 create mode 100644 src/tests/userprog/sc-bad-arg.c
 create mode 100644 src/tests/userprog/sc-bad-arg.ck
 create mode 100644 src/tests/userprog/sc-bad-sp.c
 create mode 100644 src/tests/userprog/sc-bad-sp.ck
 create mode 100644 src/tests/userprog/sc-boundary-2.c
 create mode 100644 src/tests/userprog/sc-boundary-2.ck
 create mode 100644 src/tests/userprog/sc-boundary-3.c
 create mode 100644 src/tests/userprog/sc-boundary-3.ck
 create mode 100644 src/tests/userprog/sc-boundary.c
 create mode 100644 src/tests/userprog/sc-boundary.ck
 create mode 100644 src/tests/userprog/wait-bad-pid.c
 create mode 100644 src/tests/userprog/wait-bad-pid.ck
 create mode 100644 src/tests/userprog/wait-killed.c
 create mode 100644 src/tests/userprog/wait-killed.ck
 create mode 100644 src/tests/userprog/wait-simple.c
 create mode 100644 src/tests/userprog/wait-simple.ck
 create mode 100644 src/tests/userprog/wait-twice.c
 create mode 100644 src/tests/userprog/wait-twice.ck
 create mode 100644 src/tests/userprog/write-bad-fd.c
 create mode 100644 src/tests/userprog/write-bad-fd.ck
 create mode 100644 src/tests/userprog/write-bad-ptr.c
 create mode 100644 src/tests/userprog/write-bad-ptr.ck
 create mode 100644 src/tests/userprog/write-boundary.c
 create mode 100644 src/tests/userprog/write-boundary.ck
 create mode 100644 src/tests/userprog/write-normal.c
 create mode 100644 src/tests/userprog/write-normal.ck
 create mode 100644 src/tests/userprog/write-stdin.c
 create mode 100644 src/tests/userprog/write-stdin.ck
 create mode 100644 src/tests/userprog/write-zero.c
 create mode 100644 src/tests/userprog/write-zero.ck
 create mode 100644 src/tests/vm/Grading
 create mode 100644 src/tests/vm/Make.tests
 create mode 100644 src/tests/vm/Rubric.functionality
 create mode 100644 src/tests/vm/Rubric.robustness
 create mode 100644 src/tests/vm/child-inherit.c
 create mode 100644 src/tests/vm/child-linear.c
 create mode 100644 src/tests/vm/child-mm-wrt.c
 create mode 100644 src/tests/vm/child-qsort-mm.c
 create mode 100644 src/tests/vm/child-qsort.c
 create mode 100644 src/tests/vm/child-sort.c
 create mode 100644 src/tests/vm/mmap-bad-fd.c
 create mode 100644 src/tests/vm/mmap-bad-fd.ck
 create mode 100644 src/tests/vm/mmap-clean.c
 create mode 100644 src/tests/vm/mmap-clean.ck
 create mode 100644 src/tests/vm/mmap-close.c
 create mode 100644 src/tests/vm/mmap-close.ck
 create mode 100644 src/tests/vm/mmap-exit.c
 create mode 100644 src/tests/vm/mmap-exit.ck
 create mode 100644 src/tests/vm/mmap-inherit.c
 create mode 100644 src/tests/vm/mmap-inherit.ck
 create mode 100644 src/tests/vm/mmap-misalign.c
 create mode 100644 src/tests/vm/mmap-misalign.ck
 create mode 100644 src/tests/vm/mmap-null.c
 create mode 100644 src/tests/vm/mmap-null.ck
 create mode 100644 src/tests/vm/mmap-over-code.c
 create mode 100644 src/tests/vm/mmap-over-code.ck
 create mode 100644 src/tests/vm/mmap-over-data.c
 create mode 100644 src/tests/vm/mmap-over-data.ck
 create mode 100644 src/tests/vm/mmap-over-stk.c
 create mode 100644 src/tests/vm/mmap-over-stk.ck
 create mode 100644 src/tests/vm/mmap-overlap.c
 create mode 100644 src/tests/vm/mmap-overlap.ck
 create mode 100644 src/tests/vm/mmap-read.c
 create mode 100644 src/tests/vm/mmap-read.ck
 create mode 100644 src/tests/vm/mmap-remove.c
 create mode 100644 src/tests/vm/mmap-remove.ck
 create mode 100644 src/tests/vm/mmap-shuffle.c
 create mode 100644 src/tests/vm/mmap-shuffle.ck
 create mode 100644 src/tests/vm/mmap-twice.c
 create mode 100644 src/tests/vm/mmap-twice.ck
 create mode 100644 src/tests/vm/mmap-unmap.c
 create mode 100644 src/tests/vm/mmap-unmap.ck
 create mode 100644 src/tests/vm/mmap-write.c
 create mode 100644 src/tests/vm/mmap-write.ck
 create mode 100644 src/tests/vm/mmap-zero.c
 create mode 100644 src/tests/vm/mmap-zero.ck
 create mode 100644 src/tests/vm/page-linear.c
 create mode 100644 src/tests/vm/page-linear.ck
 create mode 100644 src/tests/vm/page-merge-mm.c
 create mode 100644 src/tests/vm/page-merge-mm.ck
 create mode 100644 src/tests/vm/page-merge-par.c
 create mode 100644 src/tests/vm/page-merge-par.ck
 create mode 100644 src/tests/vm/page-merge-seq.c
 create mode 100644 src/tests/vm/page-merge-seq.ck
 create mode 100644 src/tests/vm/page-merge-stk.c
 create mode 100644 src/tests/vm/page-merge-stk.ck
 create mode 100644 src/tests/vm/page-parallel.c
 create mode 100644 src/tests/vm/page-parallel.ck
 create mode 100644 src/tests/vm/page-shuffle.c
 create mode 100644 src/tests/vm/page-shuffle.ck
 create mode 100644 src/tests/vm/parallel-merge.c
 create mode 100644 src/tests/vm/parallel-merge.h
 create mode 100644 src/tests/vm/process_death.pm
 create mode 100644 src/tests/vm/pt-bad-addr.c
 create mode 100644 src/tests/vm/pt-bad-addr.ck
 create mode 100644 src/tests/vm/pt-bad-read.c
 create mode 100644 src/tests/vm/pt-bad-read.ck
 create mode 100644 src/tests/vm/pt-big-stk-obj.c
 create mode 100644 src/tests/vm/pt-big-stk-obj.ck
 create mode 100644 src/tests/vm/pt-grow-bad.c
 create mode 100644 src/tests/vm/pt-grow-bad.ck
 create mode 100644 src/tests/vm/pt-grow-pusha.c
 create mode 100644 src/tests/vm/pt-grow-pusha.ck
 create mode 100644 src/tests/vm/pt-grow-stack.c
 create mode 100644 src/tests/vm/pt-grow-stack.ck
 create mode 100644 src/tests/vm/pt-grow-stk-sc.c
 create mode 100644 src/tests/vm/pt-grow-stk-sc.ck
 create mode 100644 src/tests/vm/pt-write-code-2.c
 create mode 100644 src/tests/vm/pt-write-code.c
 create mode 100644 src/tests/vm/pt-write-code.ck
 create mode 100644 src/tests/vm/pt-write-code2.ck
 create mode 100644 src/tests/vm/qsort.c
 create mode 100644 src/tests/vm/qsort.h
 create mode 100644 src/tests/vm/sample.inc
 create mode 100644 src/tests/vm/sample.txt
 create mode 100644 src/threads/.gitignore
 create mode 100644 src/threads/Make.vars
 create mode 100644 src/threads/Makefile
 create mode 100644 src/threads/flags.h
 create mode 100644 src/threads/init.c
 create mode 100644 src/threads/init.h
 create mode 100644 src/threads/interrupt.c
 create mode 100644 src/threads/interrupt.h
 create mode 100644 src/threads/intr-stubs.S
 create mode 100644 src/threads/intr-stubs.h
 create mode 100644 src/threads/io.h
 create mode 100644 src/threads/kernel.lds.S
 create mode 100644 src/threads/loader.S
 create mode 100644 src/threads/loader.h
 create mode 100644 src/threads/malloc.c
 create mode 100644 src/threads/malloc.h
 create mode 100644 src/threads/palloc.c
 create mode 100644 src/threads/palloc.h
 create mode 100644 src/threads/pte.h
 create mode 100644 src/threads/start.S
 create mode 100644 src/threads/switch.S
 create mode 100644 src/threads/switch.h
 create mode 100644 src/threads/synch.c
 create mode 100644 src/threads/synch.h
 create mode 100644 src/threads/thread.c
 create mode 100644 src/threads/thread.h
 create mode 100644 src/threads/vaddr.h
 create mode 100644 src/userprog/.gitignore
 create mode 100644 src/userprog/Make.vars
 create mode 100644 src/userprog/Makefile
 create mode 100644 src/userprog/exception.c
 create mode 100644 src/userprog/exception.h
 create mode 100644 src/userprog/gdt.c
 create mode 100644 src/userprog/gdt.h
 create mode 100644 src/userprog/pagedir.c
 create mode 100644 src/userprog/pagedir.h
 create mode 100644 src/userprog/process.c
 create mode 100644 src/userprog/process.h
 create mode 100644 src/userprog/syscall.c
 create mode 100644 src/userprog/syscall.h
 create mode 100644 src/userprog/tss.c
 create mode 100644 src/userprog/tss.h
 create mode 100644 src/utils/.gitignore
 create mode 100644 src/utils/Makefile
 create mode 100644 src/utils/Pintos.pm
 create mode 100644 src/utils/backtrace
 create mode 100644 src/utils/pintos
 create mode 100644 src/utils/pintos-gdb
 create mode 100644 src/utils/pintos-mkdisk
 create mode 100644 src/utils/pintos-set-cmdline
 create mode 100644 src/utils/setitimer-helper.c
 create mode 100644 src/utils/squish-pty.c
 create mode 100644 src/utils/squish-unix.c
 create mode 100644 src/vm/.gitignore
 create mode 100644 src/vm/Make.vars
 create mode 100644 src/vm/Makefile
 create mode 100644 src/vm/frame.c
 create mode 100644 src/vm/frame.h
 create mode 100644 src/vm/page.c
 create mode 100644 src/vm/page.h
 create mode 100644 src/vm/swap.c
 create mode 100644 src/vm/swap.h
 create mode 100644 src/vm/vm.tmpl

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..784002e
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,43 @@
+src							-*- text -*-
+---
+
+* Pintos core originally written by Ben Pfaff <blp@cs.stanford.edu>.
+
+* Additional features contributed by Anthony Romano <chz@vt.edu>.
+
+* The original structure and form of this operating system is inspired
+  by the Nachos system from the University of California, Berkeley.  A
+  few of the source files are more-or-less literal translations of the
+  Nachos C++ code into C.  These files bear the original UCB license
+  notice.
+
+projects
+--------
+
+* The projects are primarily the creation of Ben Pfaff
+  <blp@cs.stanford.edu>.
+
+* Godmar Back <godmar@gmail.com> made significant contributions to
+  project design.
+
+* Although little remains unchanged, the projects were originally
+  derived from those designed for Nachos by current and former CS140
+  teaching assistants at Stanford University, including at least the
+  following people:
+	
+	- Yu Ping <yph@cs.stanford.edu>
+
+	- Greg Hutchins
+
+	- Kelly Shaw <kashaw (at) cs.stanford.edu>, 
+
+	- Paul Twohey <twohey AT cs DOT stanford DOT edu>
+
+	- Sameer Qureshi <squreshi@cs.stanford.edu>
+
+	- John Rector
+
+  If you're not on this list but should be, please let me know.
+
+* Example code for condition variables is from classroom slides
+  originally by Dawson Engler and updated by Mendel Roseblum.
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..64103d0
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,56 @@
+Pintos, including its documentation, is subject to the following
+license:
+
+    Copyright (C) 2004, 2005, 2006 Board of Trustees, Leland Stanford
+    Jr. University.  All rights reserved.
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+A few individual files in Pintos were originally derived from other
+projects, but they have been extensively modified for use in Pintos.
+The original code falls under the original license, and modifications
+for Pintos are additionally covered by the Pintos license above. 
+
+In particular, code derived from Nachos is subject to the following
+license:
+
+/* Copyright (c) 1992-1996 The Regents of the University of California.
+   All rights reserved.
+
+   Permission to use, copy, modify, and distribute this software
+   and its documentation for any purpose, without fee, and
+   without written agreement is hereby granted, provided that the
+   above copyright notice and the following two paragraphs appear
+   in all copies of this software.
+
+   IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO
+   ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
+   CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE
+   AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA
+   HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
+   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+   PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
+   BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+   PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+   MODIFICATIONS.
+*/
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..0ab34c0
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,4 @@
+cscope.files
+cscope.out
+TAGS
+tags
diff --git a/src/LICENSE b/src/LICENSE
new file mode 100644
index 0000000..8702541
--- /dev/null
+++ b/src/LICENSE
@@ -0,0 +1,95 @@
+Pintos, including its documentation, is subject to the following
+license:
+
+    Copyright (C) 2004, 2005, 2006 Board of Trustees, Leland Stanford
+    Jr. University.  All rights reserved.
+
+    Permission is hereby granted, free of charge, to any person obtaining
+    a copy of this software and associated documentation files (the
+    "Software"), to deal in the Software without restriction, including
+    without limitation the rights to use, copy, modify, merge, publish,
+    distribute, sublicense, and/or sell copies of the Software, and to
+    permit persons to whom the Software is furnished to do so, subject to
+    the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+    LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+    OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+    WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+A few individual files in Pintos were originally derived from other
+projects, but they have been extensively modified for use in Pintos.
+The original code falls under the original license, and modifications
+for Pintos are additionally covered by the Pintos license above. 
+
+In particular, code derived from Nachos is subject to the following
+license:
+
+/* Copyright (c) 1992-1996 The Regents of the University of California.
+   All rights reserved.
+
+   Permission to use, copy, modify, and distribute this software
+   and its documentation for any purpose, without fee, and
+   without written agreement is hereby granted, provided that the
+   above copyright notice and the following two paragraphs appear
+   in all copies of this software.
+
+   IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO
+   ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
+   CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE
+   AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA
+   HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
+   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+   PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
+   BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+   PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+   MODIFICATIONS.
+*/
+
+Also, code derived from MIT's 6.828 course code is subject to the
+following license:
+
+/*
+ * Copyright (C) 1997 Massachusetts Institute of Technology 
+ *
+ * This software is being provided by the copyright holders under the
+ * following license. By obtaining, using and/or copying this software,
+ * you agree that you have read, understood, and will comply with the
+ * following terms and conditions:
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose and without fee or royalty is
+ * hereby granted, provided that the full text of this NOTICE appears on
+ * ALL copies of the software and documentation or portions thereof,
+ * including modifications, that you make.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY OF EXAMPLE,
+ * BUT NOT LIMITATION, COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS OR
+ * WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
+ * THAT THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY
+ * THIRD PARTY PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. COPYRIGHT
+ * HOLDERS WILL BEAR NO LIABILITY FOR ANY USE OF THIS SOFTWARE OR
+ * DOCUMENTATION.
+ *
+ * The name and trademarks of copyright holders may NOT be used in
+ * advertising or publicity pertaining to the software without specific,
+ * written prior permission. Title to copyright in this software and any
+ * associated documentation will at all times remain with copyright
+ * holders. See the file AUTHORS which should have accompanied this software
+ * for a list of all copyright holders.
+ *
+ * This file may be derived from previously copyrighted software. This
+ * copyright applies only to those changes made by the copyright
+ * holders listed in the AUTHORS file. The rest of this file is covered by
+ * the copyright notices, if any, listed below.
+ */
diff --git a/src/Make.config b/src/Make.config
new file mode 100644
index 0000000..e0afafb
--- /dev/null
+++ b/src/Make.config
@@ -0,0 +1,56 @@
+# -*- makefile -*-
+
+SHELL = /bin/sh
+
+VPATH = $(SRCDIR)
+
+# Binary utilities.
+# If the host appears to be x86, use the normal tools.
+# If it's x86-64, use the compiler and linker in 32-bit mode.
+# Otherwise assume cross-tools are installed as i386-elf-*.
+X86 = i.86\|pentium.*\|[pk][56]\|nexgen\|viac3\|6x86\|athlon.*\|i86pc
+X86_64 = x86_64
+ifneq (0, $(shell expr `uname -m` : '$(X86)'))
+  CC = gcc
+  LD = ld
+  OBJCOPY = objcopy
+else
+  ifneq (0, $(shell expr `uname -m` : '$(X86_64)'))
+    CC = gcc -m32
+    LD = ld -melf_i386
+    OBJCOPY = objcopy
+  else
+    CC = i386-elf-gcc
+    LD = i386-elf-ld
+    OBJCOPY = i386-elf-objcopy
+  endif
+endif
+
+ifeq ($(strip $(shell command -v $(CC) 2> /dev/null)),)
+$(warning *** Compiler ($(CC)) not found.  Did you set $$PATH properly?  Please refer to the Getting Started section in the documentation for details. ***)
+endif
+
+# Compiler and assembler invocation.
+DEFINES =
+WARNINGS = -Wall -W -Wstrict-prototypes -Wmissing-prototypes -Wsystem-headers
+CFLAGS = -g -msoft-float -O -march=i686
+CPPFLAGS = -nostdinc -I$(SRCDIR) -I$(SRCDIR)/lib
+ASFLAGS = -Wa,--gstabs
+LDFLAGS = -z noseparate-code
+DEPS = -MMD -MF $(@:.o=.d)
+
+# Turn off -fstack-protector, which we don't support.
+ifeq ($(strip $(shell echo | $(CC) -fno-stack-protector -E - > /dev/null 2>&1; echo $$?)),0)
+CFLAGS += -fno-stack-protector
+endif
+
+# Turn off --build-id in the linker, which confuses the Pintos loader.
+ifeq ($(strip $(shell $(LD) --help | grep -q build-id; echo $$?)),0)
+LDFLAGS += -Wl,--build-id=none
+endif
+
+%.o: %.c
+	$(CC) -c $< -o $@ $(CFLAGS) $(CPPFLAGS) $(WARNINGS) $(DEFINES) $(DEPS)
+
+%.o: %.S
+	$(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES) $(DEPS)
diff --git a/src/Makefile b/src/Makefile
new file mode 100644
index 0000000..229f85d
--- /dev/null
+++ b/src/Makefile
@@ -0,0 +1,29 @@
+BUILD_SUBDIRS = threads userprog vm filesys
+
+all::
+	@echo "Run 'make' in subdirectories: $(BUILD_SUBDIRS)."
+	@echo "This top-level make has only 'clean' targets."
+
+CLEAN_SUBDIRS = $(BUILD_SUBDIRS) examples utils
+
+clean::
+	for d in $(CLEAN_SUBDIRS); do $(MAKE) -C $$d $@; done
+	rm -f TAGS tags
+
+distclean:: clean
+	find . -name '*~' -exec rm '{}' \;
+
+TAGS_SUBDIRS = $(BUILD_SUBDIRS) devices lib
+TAGS_SOURCES = find $(TAGS_SUBDIRS) -name \*.[chS] -print
+
+TAGS::
+	etags --members `$(TAGS_SOURCES)`
+
+tags::
+	ctags -T --no-warn `$(TAGS_SOURCES)`
+
+cscope.files::
+	$(TAGS_SOURCES) > cscope.files
+
+cscope:: cscope.files
+	cscope -b -q -k
diff --git a/src/Makefile.build b/src/Makefile.build
new file mode 100644
index 0000000..fe80412
--- /dev/null
+++ b/src/Makefile.build
@@ -0,0 +1,112 @@
+# -*- makefile -*-
+
+SRCDIR = ../..
+
+all: kernel.bin loader.bin
+
+include ../../Make.config
+include ../Make.vars
+include ../../tests/Make.tests
+
+# Compiler and assembler options.
+kernel.bin: CPPFLAGS += -I$(SRCDIR)/lib/kernel
+
+# Core kernel.
+threads_SRC  = threads/start.S		# Startup code.
+threads_SRC += threads/init.c		# Main program.
+threads_SRC += threads/thread.c		# Thread management core.
+threads_SRC += threads/switch.S		# Thread switch routine.
+threads_SRC += threads/interrupt.c	# Interrupt core.
+threads_SRC += threads/intr-stubs.S	# Interrupt stubs.
+threads_SRC += threads/synch.c		# Synchronization.
+threads_SRC += threads/palloc.c		# Page allocator.
+threads_SRC += threads/malloc.c		# Subpage allocator.
+
+# Device driver code.
+devices_SRC  = devices/pit.c		# Programmable interrupt timer chip.
+devices_SRC += devices/timer.c		# Periodic timer device.
+devices_SRC += devices/kbd.c		# Keyboard device.
+devices_SRC += devices/vga.c		# Video device.
+devices_SRC += devices/serial.c		# Serial port device.
+devices_SRC += devices/block.c		# Block device abstraction layer.
+devices_SRC += devices/partition.c	# Partition block device.
+devices_SRC += devices/ide.c		# IDE disk block device.
+devices_SRC += devices/input.c		# Serial and keyboard input.
+devices_SRC += devices/intq.c		# Interrupt queue.
+devices_SRC += devices/rtc.c		# Real-time clock.
+devices_SRC += devices/shutdown.c	# Reboot and power off.
+devices_SRC += devices/speaker.c	# PC speaker.
+
+# Library code shared between kernel and user programs.
+lib_SRC  = lib/debug.c			# Debug helpers.
+lib_SRC += lib/random.c			# Pseudo-random numbers.
+lib_SRC += lib/stdio.c			# I/O library.
+lib_SRC += lib/stdlib.c			# Utility functions.
+lib_SRC += lib/string.c			# String functions.
+lib_SRC += lib/arithmetic.c		# 64-bit arithmetic for GCC.
+lib_SRC += lib/ustar.c			# Unix standard tar format utilities.
+
+# Kernel-specific library code.
+lib/kernel_SRC  = lib/kernel/debug.c	# Debug helpers.
+lib/kernel_SRC += lib/kernel/list.c	# Doubly-linked lists.
+lib/kernel_SRC += lib/kernel/bitmap.c	# Bitmaps.
+lib/kernel_SRC += lib/kernel/hash.c	# Hash tables.
+lib/kernel_SRC += lib/kernel/console.c	# printf(), putchar().
+
+# User process code.
+userprog_SRC  = userprog/process.c	# Process loading.
+userprog_SRC += userprog/pagedir.c	# Page directories.
+userprog_SRC += userprog/exception.c	# User exception handler.
+userprog_SRC += userprog/syscall.c	# System call handler.
+userprog_SRC += userprog/gdt.c		# GDT initialization.
+userprog_SRC += userprog/tss.c		# TSS management.
+
+# Virtual memory code (Newly Add for Proj03 VM).
+vm_SRC = vm/frame.c					# Frame table entry.
+vm_SRC += vm/page.c					# Page table entry.
+vm_SRC += vm/swap.c					# Swap position on disk.
+
+# Filesystem code.
+filesys_SRC  = filesys/filesys.c	# Filesystem core.
+filesys_SRC += filesys/free-map.c	# Free sector bitmap.
+filesys_SRC += filesys/file.c		# Files.
+filesys_SRC += filesys/directory.c	# Directories.
+filesys_SRC += filesys/inode.c		# File headers.
+filesys_SRC += filesys/fsutil.c		# Utilities.
+filesys_SRC += filesys/cache.c		# Buffer Cache.
+
+SOURCES = $(foreach dir,$(KERNEL_SUBDIRS),$($(dir)_SRC))
+OBJECTS = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(SOURCES)))
+DEPENDS = $(patsubst %.o,%.d,$(OBJECTS))
+
+threads/kernel.lds.s: CPPFLAGS += -P
+threads/kernel.lds.s: threads/kernel.lds.S threads/loader.h
+
+kernel.o: threads/kernel.lds.s $(OBJECTS) 
+	$(LD) -T $< -o $@ $(OBJECTS)
+
+kernel.bin: kernel.o
+	$(OBJCOPY) -R .note -R .comment -S $< $@
+
+threads/loader.o: threads/loader.S
+	$(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES)
+
+loader.bin: threads/loader.o
+	$(LD) -N -e 0 -Ttext 0x7c00 --oformat binary -o $@ $<
+
+os.dsk: kernel.bin
+	cat $^ > $@
+
+clean::
+	rm -f $(OBJECTS) $(DEPENDS) 
+	rm -f threads/loader.o threads/kernel.lds.s threads/loader.d
+	rm -f kernel.bin.tmp
+	rm -f kernel.o kernel.lds.s
+	rm -f kernel.bin loader.bin
+	rm -f bochsout.txt bochsrc.txt
+	rm -f results grade
+
+Makefile: $(SRCDIR)/Makefile.build
+	cp $< $@
+
+-include $(DEPENDS)
diff --git a/src/Makefile.kernel b/src/Makefile.kernel
new file mode 100644
index 0000000..162a411
--- /dev/null
+++ b/src/Makefile.kernel
@@ -0,0 +1,20 @@
+# -*- makefile -*-
+
+all:
+
+include Make.vars
+
+DIRS = $(sort $(addprefix build/,$(KERNEL_SUBDIRS) $(TEST_SUBDIRS) lib/user))
+
+all grade check: $(DIRS) build/Makefile
+	cd build && $(MAKE) $@
+$(DIRS):
+	mkdir -p $@
+build/Makefile: ../Makefile.build
+	cp $< $@
+
+build/%: $(DIRS) build/Makefile
+	cd build && $(MAKE) $*
+
+clean:
+	rm -rf build
diff --git a/src/Makefile.userprog b/src/Makefile.userprog
new file mode 100644
index 0000000..0df391a
--- /dev/null
+++ b/src/Makefile.userprog
@@ -0,0 +1,52 @@
+# -*- makefile -*-
+
+$(PROGS): CPPFLAGS += -I$(SRCDIR)/lib/user -I.
+
+# Linker flags.
+$(PROGS): LDFLAGS += -nostdlib -static -Wl,-T,$(LDSCRIPT)
+$(PROGS): LDSCRIPT = $(SRCDIR)/lib/user/user.lds
+
+# Library code shared between kernel and user programs.
+lib_SRC  = lib/debug.c			# Debug code.
+lib_SRC += lib/random.c			# Pseudo-random numbers.
+lib_SRC += lib/stdio.c			# I/O library.
+lib_SRC += lib/stdlib.c			# Utility functions.
+lib_SRC += lib/string.c			# String functions.
+lib_SRC += lib/arithmetic.c		# 64-bit arithmetic for GCC.
+lib_SRC += lib/ustar.c			# Unix standard tar format utilities.
+
+# User level only library code.
+lib/user_SRC  = lib/user/debug.c	# Debug helpers.
+lib/user_SRC += lib/user/syscall.c	# System calls.
+lib/user_SRC += lib/user/console.c	# Console code.
+
+LIB_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(lib_SRC) $(lib/user_SRC)))
+LIB_DEP = $(patsubst %.o,%.d,$(LIB_OBJ))
+LIB = lib/user/entry.o libc.a
+
+PROGS_SRC = $(foreach prog,$(PROGS),$($(prog)_SRC))
+PROGS_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$(PROGS_SRC)))
+PROGS_DEP = $(patsubst %.o,%.d,$(PROGS_OBJ))
+
+all: $(PROGS)
+
+define TEMPLATE
+$(1)_OBJ = $(patsubst %.c,%.o,$(patsubst %.S,%.o,$($(1)_SRC)))
+$(1): $$($(1)_OBJ) $$(LIB) $$(LDSCRIPT)
+	$$(CC) $$(LDFLAGS) $$($(1)_OBJ) $$(LIB) -o $$@
+endef
+
+$(foreach prog,$(PROGS),$(eval $(call TEMPLATE,$(prog))))
+
+libc.a: $(LIB_OBJ)
+	rm -f $@
+	ar r $@ $^
+	ranlib $@
+
+clean::
+	rm -f $(PROGS) $(PROGS_OBJ) $(PROGS_DEP)
+	rm -f $(LIB_DEP) $(LIB_OBJ) lib/user/entry.[do] libc.a 
+
+.PHONY: all clean
+
+-include $(LIB_DEP) $(PROGS_DEP)
diff --git a/src/devices/block.c b/src/devices/block.c
new file mode 100644
index 0000000..a3acec1
--- /dev/null
+++ b/src/devices/block.c
@@ -0,0 +1,223 @@
+#include "devices/block.h"
+#include <list.h>
+#include <string.h>
+#include <stdio.h>
+#include "devices/ide.h"
+#include "threads/malloc.h"
+
+/* A block device. */
+struct block
+  {
+    struct list_elem list_elem;         /* Element in all_blocks. */
+
+    char name[16];                      /* Block device name. */
+    enum block_type type;                /* Type of block device. */
+    block_sector_t size;                 /* Size in sectors. */
+
+    const struct block_operations *ops;  /* Driver operations. */
+    void *aux;                          /* Extra data owned by driver. */
+
+    unsigned long long read_cnt;        /* Number of sectors read. */
+    unsigned long long write_cnt;       /* Number of sectors written. */
+  };
+
+/* List of all block devices. */
+static struct list all_blocks = LIST_INITIALIZER (all_blocks);
+
+/* The block block assigned to each Pintos role. */
+static struct block *block_by_role[BLOCK_ROLE_CNT];
+
+static struct block *list_elem_to_block (struct list_elem *);
+
+/* Returns a human-readable name for the given block device
+   TYPE. */
+const char *
+block_type_name (enum block_type type)
+{
+  static const char *block_type_names[BLOCK_CNT] =
+    {
+      "kernel",
+      "filesys",
+      "scratch",
+      "swap",
+      "raw",
+      "foreign",
+    };
+
+  ASSERT (type < BLOCK_CNT);
+  return block_type_names[type];
+}
+
+/* Returns the block device fulfilling the given ROLE, or a null
+   pointer if no block device has been assigned that role. */
+struct block *
+block_get_role (enum block_type role)
+{
+  ASSERT (role < BLOCK_ROLE_CNT);
+  return block_by_role[role];
+}
+
+/* Assigns BLOCK the given ROLE. */
+void
+block_set_role (enum block_type role, struct block *block)
+{
+  ASSERT (role < BLOCK_ROLE_CNT);
+  block_by_role[role] = block;
+}
+
+/* Returns the first block device in kernel probe order, or a
+   null pointer if no block devices are registered. */
+struct block *
+block_first (void)
+{
+  return list_elem_to_block (list_begin (&all_blocks));
+}
+
+/* Returns the block device following BLOCK in kernel probe
+   order, or a null pointer if BLOCK is the last block device. */
+struct block *
+block_next (struct block *block)
+{
+  return list_elem_to_block (list_next (&block->list_elem));
+}
+
+/* Returns the block device with the given NAME, or a null
+   pointer if no block device has that name. */
+struct block *
+block_get_by_name (const char *name)
+{
+  struct list_elem *e;
+
+  for (e = list_begin (&all_blocks); e != list_end (&all_blocks);
+       e = list_next (e))
+    {
+      struct block *block = list_entry (e, struct block, list_elem);
+      if (!strcmp (name, block->name))
+        return block;
+    }
+
+  return NULL;
+}
+
+/* Verifies that SECTOR is a valid offset within BLOCK.
+   Panics if not. */
+static void
+check_sector (struct block *block, block_sector_t sector)
+{
+  if (sector >= block->size)
+    {
+      /* We do not use ASSERT because we want to panic here
+         regardless of whether NDEBUG is defined. */
+      PANIC ("Access past end of device %s (sector=%"PRDSNu", "
+             "size=%"PRDSNu")\n", block_name (block), sector, block->size);
+    }
+}
+
+/* Reads sector SECTOR from BLOCK into BUFFER, which must
+   have room for BLOCK_SECTOR_SIZE bytes.
+   Internally synchronizes accesses to block devices, so external
+   per-block device locking is unneeded. */
+void
+block_read (struct block *block, block_sector_t sector, void *buffer)
+{
+  check_sector (block, sector);
+  block->ops->read (block->aux, sector, buffer);
+  block->read_cnt++;
+}
+
+/* Write sector SECTOR to BLOCK from BUFFER, which must contain
+   BLOCK_SECTOR_SIZE bytes.  Returns after the block device has
+   acknowledged receiving the data.
+   Internally synchronizes accesses to block devices, so external
+   per-block device locking is unneeded. */
+void
+block_write (struct block *block, block_sector_t sector, const void *buffer)
+{
+  check_sector (block, sector);
+  ASSERT (block->type != BLOCK_FOREIGN);
+  block->ops->write (block->aux, sector, buffer);
+  block->write_cnt++;
+}
+
+/* Returns the number of sectors in BLOCK. */
+block_sector_t
+block_size (struct block *block)
+{
+  return block->size;
+}
+
+/* Returns BLOCK's name (e.g. "hda"). */
+const char *
+block_name (struct block *block)
+{
+  return block->name;
+}
+
+/* Returns BLOCK's type. */
+enum block_type
+block_type (struct block *block)
+{
+  return block->type;
+}
+
+/* Prints statistics for each block device used for a Pintos role. */
+void
+block_print_stats (void)
+{
+  int i;
+
+  for (i = 0; i < BLOCK_ROLE_CNT; i++)
+    {
+      struct block *block = block_by_role[i];
+      if (block != NULL)
+        {
+          printf ("%s (%s): %llu reads, %llu writes\n",
+                  block->name, block_type_name (block->type),
+                  block->read_cnt, block->write_cnt);
+        }
+    }
+}
+
+/* Registers a new block device with the given NAME.  If
+   EXTRA_INFO is non-null, it is printed as part of a user
+   message.  The block device's SIZE in sectors and its TYPE must
+   be provided, as well as the it operation functions OPS, which
+   will be passed AUX in each function call. */
+struct block *
+block_register (const char *name, enum block_type type,
+                const char *extra_info, block_sector_t size,
+                const struct block_operations *ops, void *aux)
+{
+  struct block *block = malloc (sizeof *block);
+  if (block == NULL)
+    PANIC ("Failed to allocate memory for block device descriptor");
+
+  list_push_back (&all_blocks, &block->list_elem);
+  strlcpy (block->name, name, sizeof block->name);
+  block->type = type;
+  block->size = size;
+  block->ops = ops;
+  block->aux = aux;
+  block->read_cnt = 0;
+  block->write_cnt = 0;
+
+  printf ("%s: %'"PRDSNu" sectors (", block->name, block->size);
+  print_human_readable_size ((uint64_t) block->size * BLOCK_SECTOR_SIZE);
+  printf (")");
+  if (extra_info != NULL)
+    printf (", %s", extra_info);
+  printf ("\n");
+
+  return block;
+}
+
+/* Returns the block device corresponding to LIST_ELEM, or a null
+   pointer if LIST_ELEM is the list end of all_blocks. */
+static struct block *
+list_elem_to_block (struct list_elem *list_elem)
+{
+  return (list_elem != list_end (&all_blocks)
+          ? list_entry (list_elem, struct block, list_elem)
+          : NULL);
+}
+
diff --git a/src/devices/block.h b/src/devices/block.h
new file mode 100644
index 0000000..21732d6
--- /dev/null
+++ b/src/devices/block.h
@@ -0,0 +1,74 @@
+#ifndef DEVICES_BLOCK_H
+#define DEVICES_BLOCK_H
+
+#include <stddef.h>
+#include <inttypes.h>
+
+/* Size of a block device sector in bytes.
+   All IDE disks use this sector size, as do most USB and SCSI
+   disks.  It's not worth it to try to cater to other sector
+   sizes in Pintos (yet). */
+#define BLOCK_SECTOR_SIZE 512
+
+/* Index of a block device sector.
+   Good enough for devices up to 2 TB. */
+typedef uint32_t block_sector_t;
+
+/* Format specifier for printf(), e.g.:
+   printf ("sector=%"PRDSNu"\n", sector); */
+#define PRDSNu PRIu32
+
+/* Higher-level interface for file systems, etc. */
+
+struct block;
+
+/* Type of a block device. */
+enum block_type
+  {
+    /* Block device types that play a role in Pintos. */
+    BLOCK_KERNEL,                /* Pintos OS kernel. */
+    BLOCK_FILESYS,               /* File system. */
+    BLOCK_SCRATCH,               /* Scratch. */
+    BLOCK_SWAP,                  /* Swap. */
+    BLOCK_ROLE_CNT,
+
+    /* Other kinds of block devices that Pintos may see but does
+       not interact with. */
+    BLOCK_RAW = BLOCK_ROLE_CNT,  /* "Raw" device with unidentified contents. */
+    BLOCK_FOREIGN,               /* Owned by non-Pintos operating system. */
+    BLOCK_CNT                    /* Number of Pintos block types. */
+  };
+
+const char *block_type_name (enum block_type);
+
+/* Finding block devices. */
+struct block *block_get_role (enum block_type);
+void block_set_role (enum block_type, struct block *);
+struct block *block_get_by_name (const char *name);
+
+struct block *block_first (void);
+struct block *block_next (struct block *);
+
+/* Block device operations. */
+block_sector_t block_size (struct block *);
+void block_read (struct block *, block_sector_t, void *);
+void block_write (struct block *, block_sector_t, const void *);
+const char *block_name (struct block *);
+enum block_type block_type (struct block *);
+
+/* Statistics. */
+void block_print_stats (void);
+
+/* Lower-level interface to block device drivers. */
+
+struct block_operations
+  {
+    void (*read) (void *aux, block_sector_t, void *buffer);
+    void (*write) (void *aux, block_sector_t, const void *buffer);
+  };
+
+struct block *block_register (const char *name, enum block_type,
+                              const char *extra_info, block_sector_t size,
+                              const struct block_operations *, void *aux);
+
+#endif /* devices/block.h */
diff --git a/src/devices/ide.c b/src/devices/ide.c
new file mode 100644
index 0000000..2cc0292
--- /dev/null
+++ b/src/devices/ide.c
@@ -0,0 +1,527 @@
+#include "devices/ide.h"
+#include <ctype.h>
+#include <debug.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include "devices/block.h"
+#include "devices/partition.h"
+#include "devices/timer.h"
+#include "threads/io.h"
+#include "threads/interrupt.h"
+#include "threads/synch.h"
+
+/* The code in this file is an interface to an ATA (IDE)
+   controller.  It attempts to comply to [ATA-3]. */
+
+/* ATA command block port addresses. */
+#define reg_data(CHANNEL) ((CHANNEL)->reg_base + 0)     /* Data. */
+#define reg_error(CHANNEL) ((CHANNEL)->reg_base + 1)    /* Error. */
+#define reg_nsect(CHANNEL) ((CHANNEL)->reg_base + 2)    /* Sector Count. */
+#define reg_lbal(CHANNEL) ((CHANNEL)->reg_base + 3)     /* LBA 0:7. */
+#define reg_lbam(CHANNEL) ((CHANNEL)->reg_base + 4)     /* LBA 15:8. */
+#define reg_lbah(CHANNEL) ((CHANNEL)->reg_base + 5)     /* LBA 23:16. */
+#define reg_device(CHANNEL) ((CHANNEL)->reg_base + 6)   /* Device/LBA 27:24. */
+#define reg_status(CHANNEL) ((CHANNEL)->reg_base + 7)   /* Status (r/o). */
+#define reg_command(CHANNEL) reg_status (CHANNEL)       /* Command (w/o). */
+
+/* ATA control block port addresses.
+   (If we supported non-legacy ATA controllers this would not be
+   flexible enough, but it's fine for what we do.) */
+#define reg_ctl(CHANNEL) ((CHANNEL)->reg_base + 0x206)  /* Control (w/o). */
+#define reg_alt_status(CHANNEL) reg_ctl (CHANNEL)       /* Alt Status (r/o). */
+
+/* Alternate Status Register bits. */
+#define STA_BSY 0x80            /* Busy. */
+#define STA_DRDY 0x40           /* Device Ready. */
+#define STA_DRQ 0x08            /* Data Request. */
+
+/* Control Register bits. */
+#define CTL_SRST 0x04           /* Software Reset. */
+
+/* Device Register bits. */
+#define DEV_MBS 0xa0            /* Must be set. */
+#define DEV_LBA 0x40            /* Linear based addressing. */
+#define DEV_DEV 0x10            /* Select device: 0=master, 1=slave. */
+
+/* Commands.
+   Many more are defined but this is the small subset that we
+   use. */
+#define CMD_IDENTIFY_DEVICE 0xec        /* IDENTIFY DEVICE. */
+#define CMD_READ_SECTOR_RETRY 0x20      /* READ SECTOR with retries. */
+#define CMD_WRITE_SECTOR_RETRY 0x30     /* WRITE SECTOR with retries. */
+
+/* An ATA device. */
+struct ata_disk
+  {
+    char name[8];               /* Name, e.g. "hda". */
+    struct channel *channel;    /* Channel that disk is attached to. */
+    int dev_no;                 /* Device 0 or 1 for master or slave. */
+    bool is_ata;                /* Is device an ATA disk? */
+  };
+
+/* An ATA channel (aka controller).
+   Each channel can control up to two disks. */
+struct channel
+  {
+    char name[8];               /* Name, e.g. "ide0". */
+    uint16_t reg_base;          /* Base I/O port. */
+    uint8_t irq;                /* Interrupt in use. */
+
+    struct lock lock;           /* Must acquire to access the controller. */
+    bool expecting_interrupt;   /* True if an interrupt is expected, false if
+                                   any interrupt would be spurious. */
+    struct semaphore completion_wait;   /* Up'd by interrupt handler. */
+
+    struct ata_disk devices[2];     /* The devices on this channel. */
+  };
+
+/* We support the two "legacy" ATA channels found in a standard PC. */
+#define CHANNEL_CNT 2
+static struct channel channels[CHANNEL_CNT];
+
+static struct block_operations ide_operations;
+
+static void reset_channel (struct channel *);
+static bool check_device_type (struct ata_disk *);
+static void identify_ata_device (struct ata_disk *);
+
+static void select_sector (struct ata_disk *, block_sector_t);
+static void issue_pio_command (struct channel *, uint8_t command);
+static void input_sector (struct channel *, void *);
+static void output_sector (struct channel *, const void *);
+
+static void wait_until_idle (const struct ata_disk *);
+static bool wait_while_busy (const struct ata_disk *);
+static void select_device (const struct ata_disk *);
+static void select_device_wait (const struct ata_disk *);
+
+static void interrupt_handler (struct intr_frame *);
+
+/* Initialize the disk subsystem and detect disks. */
+void
+ide_init (void) 
+{
+  size_t chan_no;
+
+  for (chan_no = 0; chan_no < CHANNEL_CNT; chan_no++)
+    {
+      struct channel *c = &channels[chan_no];
+      int dev_no;
+
+      /* Initialize channel. */
+      snprintf (c->name, sizeof c->name, "ide%zu", chan_no);
+      switch (chan_no) 
+        {
+        case 0:
+          c->reg_base = 0x1f0;
+          c->irq = 14 + 0x20;
+          break;
+        case 1:
+          c->reg_base = 0x170;
+          c->irq = 15 + 0x20;
+          break;
+        default:
+          NOT_REACHED ();
+        }
+      lock_init (&c->lock);
+      c->expecting_interrupt = false;
+      sema_init (&c->completion_wait, 0);
+ 
+      /* Initialize devices. */
+      for (dev_no = 0; dev_no < 2; dev_no++)
+        {
+          struct ata_disk *d = &c->devices[dev_no];
+          snprintf (d->name, sizeof d->name,
+                    "hd%c", 'a' + chan_no * 2 + dev_no); 
+          d->channel = c;
+          d->dev_no = dev_no;
+          d->is_ata = false;
+        }
+
+      /* Register interrupt handler. */
+      intr_register_ext (c->irq, interrupt_handler, c->name);
+
+      /* Reset hardware. */
+      reset_channel (c);
+
+      /* Distinguish ATA hard disks from other devices. */
+      if (check_device_type (&c->devices[0]))
+        check_device_type (&c->devices[1]);
+
+      /* Read hard disk identity information. */
+      for (dev_no = 0; dev_no < 2; dev_no++)
+        if (c->devices[dev_no].is_ata)
+          identify_ata_device (&c->devices[dev_no]);
+    }
+}
+
+/* Disk detection and identification. */
+
+static char *descramble_ata_string (char *, int size);
+
+/* Resets an ATA channel and waits for any devices present on it
+   to finish the reset. */
+static void
+reset_channel (struct channel *c) 
+{
+  bool present[2];
+  int dev_no;
+
+  /* The ATA reset sequence depends on which devices are present,
+     so we start by detecting device presence. */
+  for (dev_no = 0; dev_no < 2; dev_no++)
+    {
+      struct ata_disk *d = &c->devices[dev_no];
+
+      select_device (d);
+
+      outb (reg_nsect (c), 0x55);
+      outb (reg_lbal (c), 0xaa);
+
+      outb (reg_nsect (c), 0xaa);
+      outb (reg_lbal (c), 0x55);
+
+      outb (reg_nsect (c), 0x55);
+      outb (reg_lbal (c), 0xaa);
+
+      present[dev_no] = (inb (reg_nsect (c)) == 0x55
+                         && inb (reg_lbal (c)) == 0xaa);
+    }
+
+  /* Issue soft reset sequence, which selects device 0 as a side effect.
+     Also enable interrupts. */
+  outb (reg_ctl (c), 0);
+  timer_usleep (10);
+  outb (reg_ctl (c), CTL_SRST);
+  timer_usleep (10);
+  outb (reg_ctl (c), 0);
+
+  timer_msleep (150);
+
+  /* Wait for device 0 to clear BSY. */
+  if (present[0]) 
+    {
+      select_device (&c->devices[0]);
+      wait_while_busy (&c->devices[0]); 
+    }
+
+  /* Wait for device 1 to clear BSY. */
+  if (present[1])
+    {
+      int i;
+
+      select_device (&c->devices[1]);
+      for (i = 0; i < 3000; i++) 
+        {
+          if (inb (reg_nsect (c)) == 1 && inb (reg_lbal (c)) == 1)
+            break;
+          timer_msleep (10);
+        }
+      wait_while_busy (&c->devices[1]);
+    }
+}
+
+/* Checks whether device D is an ATA disk and sets D's is_ata
+   member appropriately.  If D is device 0 (master), returns true
+   if it's possible that a slave (device 1) exists on this
+   channel.  If D is device 1 (slave), the return value is not
+   meaningful. */
+static bool
+check_device_type (struct ata_disk *d) 
+{
+  struct channel *c = d->channel;
+  uint8_t error, lbam, lbah, status;
+
+  select_device (d);
+
+  error = inb (reg_error (c));
+  lbam = inb (reg_lbam (c));
+  lbah = inb (reg_lbah (c));
+  status = inb (reg_status (c));
+
+  if ((error != 1 && (error != 0x81 || d->dev_no == 1))
+      || (status & STA_DRDY) == 0
+      || (status & STA_BSY) != 0)
+    {
+      d->is_ata = false;
+      return error != 0x81;      
+    }
+  else 
+    {
+      d->is_ata = (lbam == 0 && lbah == 0) || (lbam == 0x3c && lbah == 0xc3);
+      return true; 
+    }
+}
+
+/* Sends an IDENTIFY DEVICE command to disk D and reads the
+   response.  Registers the disk with the block device
+   layer. */
+static void
+identify_ata_device (struct ata_disk *d) 
+{
+  struct channel *c = d->channel;
+  char id[BLOCK_SECTOR_SIZE];
+  block_sector_t capacity;
+  char *model, *serial;
+  char extra_info[128];
+  struct block *block;
+
+  ASSERT (d->is_ata);
+
+  /* Send the IDENTIFY DEVICE command, wait for an interrupt
+     indicating the device's response is ready, and read the data
+     into our buffer. */
+  select_device_wait (d);
+  issue_pio_command (c, CMD_IDENTIFY_DEVICE);
+  sema_down (&c->completion_wait);
+  if (!wait_while_busy (d))
+    {
+      d->is_ata = false;
+      return;
+    }
+  input_sector (c, id);
+
+  /* Calculate capacity.
+     Read model name and serial number. */
+  capacity = *(uint32_t *) &id[60 * 2];
+  model = descramble_ata_string (&id[10 * 2], 20);
+  serial = descramble_ata_string (&id[27 * 2], 40);
+  snprintf (extra_info, sizeof extra_info,
+            "model \"%s\", serial \"%s\"", model, serial);
+
+  /* Disable access to IDE disks over 1 GB, which are likely
+     physical IDE disks rather than virtual ones.  If we don't
+     allow access to those, we're less likely to scribble on
+     someone's important data.  You can disable this check by
+     hand if you really want to do so. */
+  if (capacity >= 1024 * 1024 * 1024 / BLOCK_SECTOR_SIZE)
+    {
+      printf ("%s: ignoring ", d->name);
+      print_human_readable_size (capacity * 512);
+      printf ("disk for safety\n");
+      d->is_ata = false;
+      return;
+    }
+
+  /* Register. */
+  block = block_register (d->name, BLOCK_RAW, extra_info, capacity,
+                          &ide_operations, d);
+  partition_scan (block);
+}
+
+/* Translates STRING, which consists of SIZE bytes in a funky
+   format, into a null-terminated string in-place.  Drops
+   trailing whitespace and null bytes.  Returns STRING.  */
+static char *
+descramble_ata_string (char *string, int size) 
+{
+  int i;
+
+  /* Swap all pairs of bytes. */
+  for (i = 0; i + 1 < size; i += 2)
+    {
+      char tmp = string[i];
+      string[i] = string[i + 1];
+      string[i + 1] = tmp;
+    }
+
+  /* Find the last non-white, non-null character. */
+  for (size--; size > 0; size--)
+    {
+      int c = string[size - 1];
+      if (c != '\0' && !isspace (c))
+        break; 
+    }
+  string[size] = '\0';
+
+  return string;
+}
+
+/* Reads sector SEC_NO from disk D into BUFFER, which must have
+   room for BLOCK_SECTOR_SIZE bytes.
+   Internally synchronizes accesses to disks, so external
+   per-disk locking is unneeded. */
+static void
+ide_read (void *d_, block_sector_t sec_no, void *buffer)
+{
+  struct ata_disk *d = d_;
+  struct channel *c = d->channel;
+  lock_acquire (&c->lock);
+  select_sector (d, sec_no);
+  issue_pio_command (c, CMD_READ_SECTOR_RETRY);
+  sema_down (&c->completion_wait);
+  if (!wait_while_busy (d))
+    PANIC ("%s: disk read failed, sector=%"PRDSNu, d->name, sec_no);
+  input_sector (c, buffer);
+  lock_release (&c->lock);
+}
+
+/* Write sector SEC_NO to disk D from BUFFER, which must contain
+   BLOCK_SECTOR_SIZE bytes.  Returns after the disk has
+   acknowledged receiving the data.
+   Internally synchronizes accesses to disks, so external
+   per-disk locking is unneeded. */
+static void
+ide_write (void *d_, block_sector_t sec_no, const void *buffer)
+{
+  struct ata_disk *d = d_;
+  struct channel *c = d->channel;
+  lock_acquire (&c->lock);
+  select_sector (d, sec_no);
+  issue_pio_command (c, CMD_WRITE_SECTOR_RETRY);
+  if (!wait_while_busy (d))
+    PANIC ("%s: disk write failed, sector=%"PRDSNu, d->name, sec_no);
+  output_sector (c, buffer);
+  sema_down (&c->completion_wait);
+  lock_release (&c->lock);
+}
+
+static struct block_operations ide_operations =
+  {
+    ide_read,
+    ide_write
+  };
+
+/* Selects device D, waiting for it to become ready, and then
+   writes SEC_NO to the disk's sector selection registers.  (We
+   use LBA mode.) */
+static void
+select_sector (struct ata_disk *d, block_sector_t sec_no)
+{
+  struct channel *c = d->channel;
+
+  ASSERT (sec_no < (1UL << 28));
+  
+  select_device_wait (d);
+  outb (reg_nsect (c), 1);
+  outb (reg_lbal (c), sec_no);
+  outb (reg_lbam (c), sec_no >> 8);
+  outb (reg_lbah (c), (sec_no >> 16));
+  outb (reg_device (c),
+        DEV_MBS | DEV_LBA | (d->dev_no == 1 ? DEV_DEV : 0) | (sec_no >> 24));
+}
+
+/* Writes COMMAND to channel C and prepares for receiving a
+   completion interrupt. */
+static void
+issue_pio_command (struct channel *c, uint8_t command) 
+{
+  /* Interrupts must be enabled or our semaphore will never be
+     up'd by the completion handler. */
+  ASSERT (intr_get_level () == INTR_ON);
+
+  c->expecting_interrupt = true;
+  outb (reg_command (c), command);
+}
+
+/* Reads a sector from channel C's data register in PIO mode into
+   SECTOR, which must have room for BLOCK_SECTOR_SIZE bytes. */
+static void
+input_sector (struct channel *c, void *sector) 
+{
+  insw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2);
+}
+
+/* Writes SECTOR to channel C's data register in PIO mode.
+   SECTOR must contain BLOCK_SECTOR_SIZE bytes. */
+static void
+output_sector (struct channel *c, const void *sector) 
+{
+  outsw (reg_data (c), sector, BLOCK_SECTOR_SIZE / 2);
+}
+
+/* Low-level ATA primitives. */
+
+/* Wait up to 10 seconds for the controller to become idle, that
+   is, for the BSY and DRQ bits to clear in the status register.
+
+   As a side effect, reading the status register clears any
+   pending interrupt. */
+static void
+wait_until_idle (const struct ata_disk *d) 
+{
+  int i;
+
+  for (i = 0; i < 1000; i++) 
+    {
+      if ((inb (reg_status (d->channel)) & (STA_BSY | STA_DRQ)) == 0)
+        return;
+      timer_usleep (10);
+    }
+
+  printf ("%s: idle timeout\n", d->name);
+}
+
+/* Wait up to 30 seconds for disk D to clear BSY,
+   and then return the status of the DRQ bit.
+   The ATA standards say that a disk may take as long as that to
+   complete its reset. */
+static bool
+wait_while_busy (const struct ata_disk *d) 
+{
+  struct channel *c = d->channel;
+  int i;
+  
+  for (i = 0; i < 3000; i++)
+    {
+      if (i == 700)
+        printf ("%s: busy, waiting...", d->name);
+      if (!(inb (reg_alt_status (c)) & STA_BSY)) 
+        {
+          if (i >= 700)
+            printf ("ok\n");
+          return (inb (reg_alt_status (c)) & STA_DRQ) != 0;
+        }
+      timer_msleep (10);
+    }
+
+  printf ("failed\n");
+  return false;
+}
+
+/* Program D's channel so that D is now the selected disk. */
+static void
+select_device (const struct ata_disk *d)
+{
+  struct channel *c = d->channel;
+  uint8_t dev = DEV_MBS;
+  if (d->dev_no == 1)
+    dev |= DEV_DEV;
+  outb (reg_device (c), dev);
+  inb (reg_alt_status (c));
+  timer_nsleep (400);
+}
+
+/* Select disk D in its channel, as select_device(), but wait for
+   the channel to become idle before and after. */
+static void
+select_device_wait (const struct ata_disk *d) 
+{
+  wait_until_idle (d);
+  select_device (d);
+  wait_until_idle (d);
+}
+
+/* ATA interrupt handler. */
+static void
+interrupt_handler (struct intr_frame *f) 
+{
+  struct channel *c;
+
+  for (c = channels; c < channels + CHANNEL_CNT; c++)
+    if (f->vec_no == c->irq)
+      {
+        if (c->expecting_interrupt) 
+          {
+            inb (reg_status (c));               /* Acknowledge interrupt. */
+            sema_up (&c->completion_wait);      /* Wake up waiter. */
+          }
+        else
+          printf ("%s: unexpected interrupt\n", c->name);
+        return;
+      }
+
+  NOT_REACHED ();
+}
+
+
diff --git a/src/devices/ide.h b/src/devices/ide.h
new file mode 100644
index 0000000..b35da5e
--- /dev/null
+++ b/src/devices/ide.h
@@ -0,0 +1,6 @@
+#ifndef DEVICES_IDE_H
+#define DEVICES_IDE_H
+
+void ide_init (void);
+
+#endif /* devices/ide.h */
diff --git a/src/devices/input.c b/src/devices/input.c
new file mode 100644
index 0000000..4a12160
--- /dev/null
+++ b/src/devices/input.c
@@ -0,0 +1,52 @@
+#include "devices/input.h"
+#include <debug.h>
+#include "devices/intq.h"
+#include "devices/serial.h"
+
+/* Stores keys from the keyboard and serial port. */
+static struct intq buffer;
+
+/* Initializes the input buffer. */
+void
+input_init (void) 
+{
+  intq_init (&buffer);
+}
+
+/* Adds a key to the input buffer.
+   Interrupts must be off and the buffer must not be full. */
+void
+input_putc (uint8_t key) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+  ASSERT (!intq_full (&buffer));
+
+  intq_putc (&buffer, key);
+  serial_notify ();
+}
+
+/* Retrieves a key from the input buffer.
+   If the buffer is empty, waits for a key to be pressed. */
+uint8_t
+input_getc (void) 
+{
+  enum intr_level old_level;
+  uint8_t key;
+
+  old_level = intr_disable ();
+  key = intq_getc (&buffer);
+  serial_notify ();
+  intr_set_level (old_level);
+  
+  return key;
+}
+
+/* Returns true if the input buffer is full,
+   false otherwise.
+   Interrupts must be off. */
+bool
+input_full (void) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+  return intq_full (&buffer);
+}
diff --git a/src/devices/input.h b/src/devices/input.h
new file mode 100644
index 0000000..a2f50e9
--- /dev/null
+++ b/src/devices/input.h
@@ -0,0 +1,12 @@
+#ifndef DEVICES_INPUT_H
+#define DEVICES_INPUT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+void input_init (void);
+void input_putc (uint8_t);
+uint8_t input_getc (void);
+bool input_full (void);
+
+#endif /* devices/input.h */
diff --git a/src/devices/intq.c b/src/devices/intq.c
new file mode 100644
index 0000000..40b23ae
--- /dev/null
+++ b/src/devices/intq.c
@@ -0,0 +1,114 @@
+#include "devices/intq.h"
+#include <debug.h>
+#include "threads/thread.h"
+
+static int next (int pos);
+static void wait (struct intq *q, struct thread **waiter);
+static void signal (struct intq *q, struct thread **waiter);
+
+/* Initializes interrupt queue Q. */
+void
+intq_init (struct intq *q) 
+{
+  lock_init (&q->lock);
+  q->not_full = q->not_empty = NULL;
+  q->head = q->tail = 0;
+}
+
+/* Returns true if Q is empty, false otherwise. */
+bool
+intq_empty (const struct intq *q) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+  return q->head == q->tail;
+}
+
+/* Returns true if Q is full, false otherwise. */
+bool
+intq_full (const struct intq *q) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+  return next (q->head) == q->tail;
+}
+
+/* Removes a byte from Q and returns it.
+   If Q is empty, sleeps until a byte is added.
+   When called from an interrupt handler, Q must not be empty. */
+uint8_t
+intq_getc (struct intq *q) 
+{
+  uint8_t byte;
+  
+  ASSERT (intr_get_level () == INTR_OFF);
+  while (intq_empty (q)) 
+    {
+      ASSERT (!intr_context ());
+      lock_acquire (&q->lock);
+      wait (q, &q->not_empty);
+      lock_release (&q->lock);
+    }
+  
+  byte = q->buf[q->tail];
+  q->tail = next (q->tail);
+  signal (q, &q->not_full);
+  return byte;
+}
+
+/* Adds BYTE to the end of Q.
+   If Q is full, sleeps until a byte is removed.
+   When called from an interrupt handler, Q must not be full. */
+void
+intq_putc (struct intq *q, uint8_t byte) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+  while (intq_full (q))
+    {
+      ASSERT (!intr_context ());
+      lock_acquire (&q->lock);
+      wait (q, &q->not_full);
+      lock_release (&q->lock);
+    }
+
+  q->buf[q->head] = byte;
+  q->head = next (q->head);
+  signal (q, &q->not_empty);
+}
+
+/* Returns the position after POS within an intq. */
+static int
+next (int pos) 
+{
+  return (pos + 1) % INTQ_BUFSIZE;
+}
+
+/* WAITER must be the address of Q's not_empty or not_full
+   member.  Waits until the given condition is true. */
+static void
+wait (struct intq *q UNUSED, struct thread **waiter) 
+{
+  ASSERT (!intr_context ());
+  ASSERT (intr_get_level () == INTR_OFF);
+  ASSERT ((waiter == &q->not_empty && intq_empty (q))
+          || (waiter == &q->not_full && intq_full (q)));
+
+  *waiter = thread_current ();
+  thread_block ();
+}
+
+/* WAITER must be the address of Q's not_empty or not_full
+   member, and the associated condition must be true.  If a
+   thread is waiting for the condition, wakes it up and resets
+   the waiting thread. */
+static void
+signal (struct intq *q UNUSED, struct thread **waiter) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+  ASSERT ((waiter == &q->not_empty && !intq_empty (q))
+          || (waiter == &q->not_full && !intq_full (q)));
+
+  if (*waiter != NULL) 
+    {
+      thread_unblock (*waiter);
+      *waiter = NULL;
+    }
+}
diff --git a/src/devices/intq.h b/src/devices/intq.h
new file mode 100644
index 0000000..2312b12
--- /dev/null
+++ b/src/devices/intq.h
@@ -0,0 +1,43 @@
+#ifndef DEVICES_INTQ_H
+#define DEVICES_INTQ_H
+
+#include "threads/interrupt.h"
+#include "threads/synch.h"
+
+/* An "interrupt queue", a circular buffer shared between
+   kernel threads and external interrupt handlers.
+
+   Interrupt queue functions can be called from kernel threads or
+   from external interrupt handlers.  Except for intq_init(),
+   interrupts must be off in either case.
+
+   The interrupt queue has the structure of a "monitor".  Locks
+   and condition variables from threads/synch.h cannot be used in
+   this case, as they normally would, because they can only
+   protect kernel threads from one another, not from interrupt
+   handlers. */
+
+/* Queue buffer size, in bytes. */
+#define INTQ_BUFSIZE 64
+
+/* A circular queue of bytes. */
+struct intq
+  {
+    /* Waiting threads. */
+    struct lock lock;           /* Only one thread may wait at once. */
+    struct thread *not_full;    /* Thread waiting for not-full condition. */
+    struct thread *not_empty;   /* Thread waiting for not-empty condition. */
+
+    /* Queue. */
+    uint8_t buf[INTQ_BUFSIZE];  /* Buffer. */
+    int head;                   /* New data is written here. */
+    int tail;                   /* Old data is read here. */
+  };
+
+void intq_init (struct intq *);
+bool intq_empty (const struct intq *);
+bool intq_full (const struct intq *);
+uint8_t intq_getc (struct intq *);
+void intq_putc (struct intq *, uint8_t);
+
+#endif /* devices/intq.h */
diff --git a/src/devices/kbd.c b/src/devices/kbd.c
new file mode 100644
index 0000000..fcc82be
--- /dev/null
+++ b/src/devices/kbd.c
@@ -0,0 +1,213 @@
+#include "devices/kbd.h"
+#include <ctype.h>
+#include <debug.h>
+#include <stdio.h>
+#include <string.h>
+#include "devices/input.h"
+#include "devices/shutdown.h"
+#include "threads/interrupt.h"
+#include "threads/io.h"
+
+/* Keyboard data register port. */
+#define DATA_REG 0x60
+
+/* Current state of shift keys.
+   True if depressed, false otherwise. */
+static bool left_shift, right_shift;    /* Left and right Shift keys. */
+static bool left_alt, right_alt;        /* Left and right Alt keys. */
+static bool left_ctrl, right_ctrl;      /* Left and right Ctl keys. */
+
+/* Status of Caps Lock.
+   True when on, false when off. */
+static bool caps_lock;
+
+/* Number of keys pressed. */
+static int64_t key_cnt;
+
+static intr_handler_func keyboard_interrupt;
+
+/* Initializes the keyboard. */
+void
+kbd_init (void) 
+{
+  intr_register_ext (0x21, keyboard_interrupt, "8042 Keyboard");
+}
+
+/* Prints keyboard statistics. */
+void
+kbd_print_stats (void) 
+{
+  printf ("Keyboard: %lld keys pressed\n", key_cnt);
+}
+
+/* Maps a set of contiguous scancodes into characters. */
+struct keymap
+  {
+    uint8_t first_scancode;     /* First scancode. */
+    const char *chars;          /* chars[0] has scancode first_scancode,
+                                   chars[1] has scancode first_scancode + 1,
+                                   and so on to the end of the string. */
+  };
+  
+/* Keys that produce the same characters regardless of whether
+   the Shift keys are down.  Case of letters is an exception
+   that we handle elsewhere.  */
+static const struct keymap invariant_keymap[] = 
+  {
+    {0x01, "\033"},             /* Escape. */
+    {0x0e, "\b"},
+    {0x0f, "\tQWERTYUIOP"},
+    {0x1c, "\r"},
+    {0x1e, "ASDFGHJKL"},
+    {0x2c, "ZXCVBNM"},
+    {0x37, "*"},
+    {0x39, " "},
+    {0x53, "\177"},             /* Delete. */
+    {0, NULL},
+  };
+
+/* Characters for keys pressed without Shift, for those keys
+   where it matters. */
+static const struct keymap unshifted_keymap[] = 
+  {
+    {0x02, "1234567890-="},
+    {0x1a, "[]"},
+    {0x27, ";'`"},
+    {0x2b, "\\"},
+    {0x33, ",./"},
+    {0, NULL},
+  };
+  
+/* Characters for keys pressed with Shift, for those keys where
+   it matters. */
+static const struct keymap shifted_keymap[] = 
+  {
+    {0x02, "!@#$%^&*()_+"},
+    {0x1a, "{}"},
+    {0x27, ":\"~"},
+    {0x2b, "|"},
+    {0x33, "<>?"},
+    {0, NULL},
+  };
+
+static bool map_key (const struct keymap[], unsigned scancode, uint8_t *);
+
+static void
+keyboard_interrupt (struct intr_frame *args UNUSED) 
+{
+  /* Status of shift keys. */
+  bool shift = left_shift || right_shift;
+  bool alt = left_alt || right_alt;
+  bool ctrl = left_ctrl || right_ctrl;
+
+  /* Keyboard scancode. */
+  unsigned code;
+
+  /* False if key pressed, true if key released. */
+  bool release;
+
+  /* Character that corresponds to `code'. */
+  uint8_t c;
+
+  /* Read scancode, including second byte if prefix code. */
+  code = inb (DATA_REG);
+  if (code == 0xe0)
+    code = (code << 8) | inb (DATA_REG);
+
+  /* Bit 0x80 distinguishes key press from key release
+     (even if there's a prefix). */
+  release = (code & 0x80) != 0;
+  code &= ~0x80u;
+
+  /* Interpret key. */
+  if (code == 0x3a) 
+    {
+      /* Caps Lock. */
+      if (!release)
+        caps_lock = !caps_lock;
+    }
+  else if (map_key (invariant_keymap, code, &c)
+           || (!shift && map_key (unshifted_keymap, code, &c))
+           || (shift && map_key (shifted_keymap, code, &c)))
+    {
+      /* Ordinary character. */
+      if (!release) 
+        {
+          /* Reboot if Ctrl+Alt+Del pressed. */
+          if (c == 0177 && ctrl && alt)
+            shutdown_reboot ();
+
+          /* Handle Ctrl, Shift.
+             Note that Ctrl overrides Shift. */
+          if (ctrl && c >= 0x40 && c < 0x60) 
+            {
+              /* A is 0x41, Ctrl+A is 0x01, etc. */
+              c -= 0x40; 
+            }
+          else if (shift == caps_lock)
+            c = tolower (c);
+
+          /* Handle Alt by setting the high bit.
+             This 0x80 is unrelated to the one used to
+             distinguish key press from key release. */
+          if (alt)
+            c += 0x80;
+
+          /* Append to keyboard buffer. */
+          if (!input_full ())
+            {
+              key_cnt++;
+              input_putc (c);
+            }
+        }
+    }
+  else
+    {
+      /* Maps a keycode into a shift state variable. */
+      struct shift_key 
+        {
+          unsigned scancode;
+          bool *state_var;
+        };
+
+      /* Table of shift keys. */
+      static const struct shift_key shift_keys[] = 
+        {
+          {  0x2a, &left_shift},
+          {  0x36, &right_shift},
+          {  0x38, &left_alt},
+          {0xe038, &right_alt},
+          {  0x1d, &left_ctrl},
+          {0xe01d, &right_ctrl},
+          {0,      NULL},
+        };
+  
+      const struct shift_key *key;
+
+      /* Scan the table. */
+      for (key = shift_keys; key->scancode != 0; key++) 
+        if (key->scancode == code)
+          {
+            *key->state_var = !release;
+            break;
+          }
+    }
+}
+
+/* Scans the array of keymaps K for SCANCODE.
+   If found, sets *C to the corresponding character and returns
+   true.
+   If not found, returns false and C is ignored. */
+static bool
+map_key (const struct keymap k[], unsigned scancode, uint8_t *c) 
+{
+  for (; k->first_scancode != 0; k++)
+    if (scancode >= k->first_scancode
+        && scancode < k->first_scancode + strlen (k->chars)) 
+      {
+        *c = k->chars[scancode - k->first_scancode];
+        return true; 
+      }
+
+  return false;
+}
diff --git a/src/devices/kbd.h b/src/devices/kbd.h
new file mode 100644
index 0000000..ed9c06b
--- /dev/null
+++ b/src/devices/kbd.h
@@ -0,0 +1,9 @@
+#ifndef DEVICES_KBD_H
+#define DEVICES_KBD_H
+
+#include <stdint.h>
+
+void kbd_init (void);
+void kbd_print_stats (void);
+
+#endif /* devices/kbd.h */
diff --git a/src/devices/partition.c b/src/devices/partition.c
new file mode 100644
index 0000000..7e97332
--- /dev/null
+++ b/src/devices/partition.c
@@ -0,0 +1,324 @@
+#include "devices/partition.h"
+#include <packed.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include "devices/block.h"
+#include "threads/malloc.h"
+
+/* A partition of a block device. */
+struct partition
+  {
+    struct block *block;                /* Underlying block device. */
+    block_sector_t start;               /* First sector within device. */
+  };
+
+static struct block_operations partition_operations;
+
+static void read_partition_table (struct block *, block_sector_t sector,
+                                  block_sector_t primary_extended_sector,
+                                  int *part_nr);
+static void found_partition (struct block *, uint8_t type,
+                             block_sector_t start, block_sector_t size,
+                             int part_nr);
+static const char *partition_type_name (uint8_t);
+
+/* Scans BLOCK for partitions of interest to Pintos. */
+void
+partition_scan (struct block *block)
+{
+  int part_nr = 0;
+  read_partition_table (block, 0, 0, &part_nr);
+  if (part_nr == 0)
+    printf ("%s: Device contains no partitions\n", block_name (block));
+}
+
+/* Reads the partition table in the given SECTOR of BLOCK and
+   scans it for partitions of interest to Pintos.
+
+   If SECTOR is 0, so that this is the top-level partition table
+   on BLOCK, then PRIMARY_EXTENDED_SECTOR is not meaningful;
+   otherwise, it should designate the sector of the top-level
+   extended partition table that was traversed to arrive at
+   SECTOR, for use in finding logical partitions (see the large
+   comment below).
+
+   PART_NR points to the number of non-empty primary or logical
+   partitions already encountered on BLOCK.  It is incremented as
+   partitions are found. */
+static void
+read_partition_table (struct block *block, block_sector_t sector,
+                      block_sector_t primary_extended_sector,
+                      int *part_nr)
+{
+  /* Format of a partition table entry.  See [Partitions]. */
+  struct partition_table_entry
+    {
+      uint8_t bootable;         /* 0x00=not bootable, 0x80=bootable. */
+      uint8_t start_chs[3];     /* Encoded starting cylinder, head, sector. */
+      uint8_t type;             /* Partition type (see partition_type_name). */
+      uint8_t end_chs[3];       /* Encoded ending cylinder, head, sector. */
+      uint32_t offset;          /* Start sector offset from partition table. */
+      uint32_t size;            /* Number of sectors. */
+    }
+  PACKED;
+
+  /* Partition table sector. */
+  struct partition_table
+    {
+      uint8_t loader[446];      /* Loader, in top-level partition table. */
+      struct partition_table_entry partitions[4];       /* Table entries. */
+      uint16_t signature;       /* Should be 0xaa55. */
+    }
+  PACKED;
+
+  struct partition_table *pt;
+  size_t i;
+
+  /* Check SECTOR validity. */
+  if (sector >= block_size (block))
+    {
+      printf ("%s: Partition table at sector %"PRDSNu" past end of device.\n",
+              block_name (block), sector);
+      return;
+    }
+
+  /* Read sector. */
+  ASSERT (sizeof *pt == BLOCK_SECTOR_SIZE);
+  pt = malloc (sizeof *pt);
+  if (pt == NULL)
+    PANIC ("Failed to allocate memory for partition table.");
+  block_read (block, 0, pt);
+
+  /* Check signature. */
+  if (pt->signature != 0xaa55)
+    {
+      if (primary_extended_sector == 0)
+        printf ("%s: Invalid partition table signature\n", block_name (block));
+      else
+        printf ("%s: Invalid extended partition table in sector %"PRDSNu"\n",
+                block_name (block), sector);
+      free (pt);
+      return;
+    }
+
+  /* Parse partitions. */
+  for (i = 0; i < sizeof pt->partitions / sizeof *pt->partitions; i++)
+    {
+      struct partition_table_entry *e = &pt->partitions[i];
+
+      if (e->size == 0 || e->type == 0)
+        {
+          /* Ignore empty partition. */
+        }
+      else if (e->type == 0x05       /* Extended partition. */
+               || e->type == 0x0f    /* Windows 98 extended partition. */
+               || e->type == 0x85    /* Linux extended partition. */
+               || e->type == 0xc5)   /* DR-DOS extended partition. */
+        {
+          printf ("%s: Extended partition in sector %"PRDSNu"\n",
+                  block_name (block), sector);
+
+          /* The interpretation of the offset field for extended
+             partitions is bizarre.  When the extended partition
+             table entry is in the master boot record, that is,
+             the device's primary partition table in sector 0, then
+             the offset is an absolute sector number.  Otherwise,
+             no matter how deep the partition table we're reading
+             is nested, the offset is relative to the start of
+             the extended partition that the MBR points to. */
+          if (sector == 0)
+            read_partition_table (block, e->offset, e->offset, part_nr);
+          else
+            read_partition_table (block, e->offset + primary_extended_sector,
+                                  primary_extended_sector, part_nr);
+        }
+      else
+        {
+          ++*part_nr;
+
+          found_partition (block, e->type, e->offset + sector,
+                           e->size, *part_nr);
+        }
+    }
+
+  free (pt);
+}
+
+/* We have found a primary or logical partition of the given TYPE
+   on BLOCK, starting at sector START and continuing for SIZE
+   sectors, which we are giving the partition number PART_NR.
+   Check whether this is a partition of interest to Pintos, and
+   if so then add it to the proper element of partitions[]. */
+static void
+found_partition (struct block *block, uint8_t part_type,
+                 block_sector_t start, block_sector_t size,
+                 int part_nr)
+{
+  if (start >= block_size (block))
+    printf ("%s%d: Partition starts past end of device (sector %"PRDSNu")\n",
+            block_name (block), part_nr, start);
+  else if (start + size < start || start + size > block_size (block))
+    printf ("%s%d: Partition end (%"PRDSNu") past end of device (%"PRDSNu")\n",
+            block_name (block), part_nr, start + size, block_size (block));
+  else
+    {
+      enum block_type type = (part_type == 0x20 ? BLOCK_KERNEL
+                              : part_type == 0x21 ? BLOCK_FILESYS
+                              : part_type == 0x22 ? BLOCK_SCRATCH
+                              : part_type == 0x23 ? BLOCK_SWAP
+                              : BLOCK_FOREIGN);
+      struct partition *p;
+      char extra_info[128];
+      char name[16];
+
+      p = malloc (sizeof *p);
+      if (p == NULL)
+        PANIC ("Failed to allocate memory for partition descriptor");
+      p->block = block;
+      p->start = start;
+
+      snprintf (name, sizeof name, "%s%d", block_name (block), part_nr);
+      snprintf (extra_info, sizeof extra_info, "%s (%02x)",
+                partition_type_name (part_type), part_type);
+      block_register (name, type, extra_info, size, &partition_operations, p);
+    }
+}
+
+/* Returns a human-readable name for the given partition TYPE. */
+static const char *
+partition_type_name (uint8_t type)
+{
+  /* Name of each known type of partition.
+     From util-linux-2.12r/fdisk/i386_sys_types.c.
+     This initializer makes use of a C99 feature that allows
+     array elements to be initialized by index. */
+  static const char *type_names[256] =
+    {
+      [0x00] = "Empty",
+      [0x01] = "FAT12",
+      [0x02] = "XENIX root",
+      [0x03] = "XENIX usr",
+      [0x04] = "FAT16 <32M",
+      [0x05] = "Extended",
+      [0x06] = "FAT16",
+      [0x07] = "HPFS/NTFS",
+      [0x08] = "AIX",
+      [0x09] = "AIX bootable",
+      [0x0a] = "OS/2 Boot Manager",
+      [0x0b] = "W95 FAT32",
+      [0x0c] = "W95 FAT32 (LBA)",
+      [0x0e] = "W95 FAT16 (LBA)",
+      [0x0f] = "W95 Ext'd (LBA)",
+      [0x10] = "OPUS",
+      [0x11] = "Hidden FAT12",
+      [0x12] = "Compaq diagnostics",
+      [0x14] = "Hidden FAT16 <32M",
+      [0x16] = "Hidden FAT16",
+      [0x17] = "Hidden HPFS/NTFS",
+      [0x18] = "AST SmartSleep",
+      [0x1b] = "Hidden W95 FAT32",
+      [0x1c] = "Hidden W95 FAT32 (LBA)",
+      [0x1e] = "Hidden W95 FAT16 (LBA)",
+      [0x20] = "Pintos OS kernel",
+      [0x21] = "Pintos file system",
+      [0x22] = "Pintos scratch",
+      [0x23] = "Pintos swap",
+      [0x24] = "NEC DOS",
+      [0x39] = "Plan 9",
+      [0x3c] = "PartitionMagic recovery",
+      [0x40] = "Venix 80286",
+      [0x41] = "PPC PReP Boot",
+      [0x42] = "SFS",
+      [0x4d] = "QNX4.x",
+      [0x4e] = "QNX4.x 2nd part",
+      [0x4f] = "QNX4.x 3rd part",
+      [0x50] = "OnTrack DM",
+      [0x51] = "OnTrack DM6 Aux1",
+      [0x52] = "CP/M",
+      [0x53] = "OnTrack DM6 Aux3",
+      [0x54] = "OnTrackDM6",
+      [0x55] = "EZ-Drive",
+      [0x56] = "Golden Bow",
+      [0x5c] = "Priam Edisk",
+      [0x61] = "SpeedStor",
+      [0x63] = "GNU HURD or SysV",
+      [0x64] = "Novell Netware 286",
+      [0x65] = "Novell Netware 386",
+      [0x70] = "DiskSecure Multi-Boot",
+      [0x75] = "PC/IX",
+      [0x80] = "Old Minix",
+      [0x81] = "Minix / old Linux",
+      [0x82] = "Linux swap / Solaris",
+      [0x83] = "Linux",
+      [0x84] = "OS/2 hidden C: drive",
+      [0x85] = "Linux extended",
+      [0x86] = "NTFS volume set",
+      [0x87] = "NTFS volume set",
+      [0x88] = "Linux plaintext",
+      [0x8e] = "Linux LVM",
+      [0x93] = "Amoeba",
+      [0x94] = "Amoeba BBT",
+      [0x9f] = "BSD/OS",
+      [0xa0] = "IBM Thinkpad hibernation",
+      [0xa5] = "FreeBSD",
+      [0xa6] = "OpenBSD",
+      [0xa7] = "NeXTSTEP",
+      [0xa8] = "Darwin UFS",
+      [0xa9] = "NetBSD",
+      [0xab] = "Darwin boot",
+      [0xb7] = "BSDI fs",
+      [0xb8] = "BSDI swap",
+      [0xbb] = "Boot Wizard hidden",
+      [0xbe] = "Solaris boot",
+      [0xbf] = "Solaris",
+      [0xc1] = "DRDOS/sec (FAT-12)",
+      [0xc4] = "DRDOS/sec (FAT-16 < 32M)",
+      [0xc6] = "DRDOS/sec (FAT-16)",
+      [0xc7] = "Syrinx",
+      [0xda] = "Non-FS data",
+      [0xdb] = "CP/M / CTOS / ...",
+      [0xde] = "Dell Utility",
+      [0xdf] = "BootIt",
+      [0xe1] = "DOS access",
+      [0xe3] = "DOS R/O",
+      [0xe4] = "SpeedStor",
+      [0xeb] = "BeOS fs",
+      [0xee] = "EFI GPT",
+      [0xef] = "EFI (FAT-12/16/32)",
+      [0xf0] = "Linux/PA-RISC boot",
+      [0xf1] = "SpeedStor",
+      [0xf4] = "SpeedStor",
+      [0xf2] = "DOS secondary",
+      [0xfd] = "Linux raid autodetect",
+      [0xfe] = "LANstep",
+      [0xff] = "BBT",
+    };
+
+  return type_names[type] != NULL ? type_names[type] : "Unknown";
+}
+
+/* Reads sector SECTOR from partition P into BUFFER, which must
+   have room for BLOCK_SECTOR_SIZE bytes. */
+static void
+partition_read (void *p_, block_sector_t sector, void *buffer)
+{
+  struct partition *p = p_;
+  block_read (p->block, p->start + sector, buffer);
+}
+
+/* Write sector SECTOR to partition P from BUFFER, which must
+   contain BLOCK_SECTOR_SIZE bytes.  Returns after the block has
+   acknowledged receiving the data. */
+static void
+partition_write (void *p_, block_sector_t sector, const void *buffer)
+{
+  struct partition *p = p_;
+  block_write (p->block, p->start + sector, buffer);
+}
+
+static struct block_operations partition_operations =
+  {
+    partition_read,
+    partition_write
+  };
diff --git a/src/devices/partition.h b/src/devices/partition.h
new file mode 100644
index 0000000..47fea4d
--- /dev/null
+++ b/src/devices/partition.h
@@ -0,0 +1,8 @@
+#ifndef DEVICES_PARTITION_H
+#define DEVICES_PARTITION_H
+
+struct block;
+
+void partition_scan (struct block *);
+
+#endif /* devices/partition.h */
diff --git a/src/devices/pit.c b/src/devices/pit.c
new file mode 100644
index 0000000..bfb1889
--- /dev/null
+++ b/src/devices/pit.c
@@ -0,0 +1,83 @@
+#include "devices/pit.h"
+#include <debug.h>
+#include <stdint.h>
+#include "threads/interrupt.h"
+#include "threads/io.h"
+
+/* Interface to 8254 Programmable Interrupt Timer (PIT).
+   Refer to [8254] for details. */
+
+/* 8254 registers. */
+#define PIT_PORT_CONTROL          0x43                /* Control port. */
+#define PIT_PORT_COUNTER(CHANNEL) (0x40 + (CHANNEL))  /* Counter port. */
+
+/* PIT cycles per second. */
+#define PIT_HZ 1193180
+
+/* Configure the given CHANNEL in the PIT.  In a PC, the PIT's
+   three output channels are hooked up like this:
+
+     - Channel 0 is connected to interrupt line 0, so that it can
+       be used as a periodic timer interrupt, as implemented in
+       Pintos in devices/timer.c.
+
+     - Channel 1 is used for dynamic RAM refresh (in older PCs).
+       No good can come of messing with this.
+
+     - Channel 2 is connected to the PC speaker, so that it can
+       be used to play a tone, as implemented in Pintos in
+       devices/speaker.c.
+
+   MODE specifies the form of output:
+
+     - Mode 2 is a periodic pulse: the channel's output is 1 for
+       most of the period, but drops to 0 briefly toward the end
+       of the period.  This is useful for hooking up to an
+       interrupt controller to generate a periodic interrupt.
+
+     - Mode 3 is a square wave: for the first half of the period
+       it is 1, for the second half it is 0.  This is useful for
+       generating a tone on a speaker.
+
+     - Other modes are less useful.
+
+   FREQUENCY is the number of periods per second, in Hz. */
+void
+pit_configure_channel (int channel, int mode, int frequency)
+{
+  uint16_t count;
+  enum intr_level old_level;
+
+  ASSERT (channel == 0 || channel == 2);
+  ASSERT (mode == 2 || mode == 3);
+
+  /* Convert FREQUENCY to a PIT counter value.  The PIT has a
+     clock that runs at PIT_HZ cycles per second.  We must
+     translate FREQUENCY into a number of these cycles. */
+  if (frequency < 19)
+    {
+      /* Frequency is too low: the quotient would overflow the
+         16-bit counter.  Force it to 0, which the PIT treats as
+         65536, the highest possible count.  This yields a 18.2
+         Hz timer, approximately. */
+      count = 0;
+    }
+  else if (frequency > PIT_HZ)
+    {
+      /* Frequency is too high: the quotient would underflow to
+         0, which the PIT would interpret as 65536.  A count of 1
+         is illegal in mode 2, so we force it to 2, which yields
+         a 596.590 kHz timer, approximately.  (This timer rate is
+         probably too fast to be useful anyhow.) */
+      count = 2;
+    }
+  else
+    count = (PIT_HZ + frequency / 2) / frequency;
+
+  /* Configure the PIT mode and load its counters. */
+  old_level = intr_disable ();
+  outb (PIT_PORT_CONTROL, (channel << 6) | 0x30 | (mode << 1));
+  outb (PIT_PORT_COUNTER (channel), count);
+  outb (PIT_PORT_COUNTER (channel), count >> 8);
+  intr_set_level (old_level);
+}
diff --git a/src/devices/pit.h b/src/devices/pit.h
new file mode 100644
index 0000000..dff36ae
--- /dev/null
+++ b/src/devices/pit.h
@@ -0,0 +1,8 @@
+#ifndef DEVICES_PIT_H
+#define DEVICES_PIT_H
+
+#include <stdint.h>
+
+void pit_configure_channel (int channel, int mode, int frequency);
+
+#endif /* devices/pit.h */
diff --git a/src/devices/rtc.c b/src/devices/rtc.c
new file mode 100644
index 0000000..745d779
--- /dev/null
+++ b/src/devices/rtc.c
@@ -0,0 +1,113 @@
+#include "devices/rtc.h"
+#include <round.h>
+#include <stdio.h>
+#include "threads/io.h"
+
+/* This code is an interface to the MC146818A-compatible real
+   time clock found on PC motherboards.  See [MC146818A] for
+   hardware details. */
+
+/* I/O register addresses. */
+#define CMOS_REG_SET	0x70    /* Selects CMOS register exposed by REG_IO. */
+#define CMOS_REG_IO	0x71    /* Contains the selected data byte. */
+
+/* Indexes of CMOS registers with real-time clock functions.
+   Note that all of these registers are in BCD format,
+   so that 0x59 means 59, not 89. */
+#define RTC_REG_SEC	0       /* Second: 0x00...0x59. */
+#define RTC_REG_MIN	2       /* Minute: 0x00...0x59. */
+#define RTC_REG_HOUR	4       /* Hour: 0x00...0x23. */
+#define RTC_REG_MDAY	7	/* Day of the month: 0x01...0x31. */
+#define RTC_REG_MON	8       /* Month: 0x01...0x12. */
+#define RTC_REG_YEAR	9	/* Year: 0x00...0x99. */
+
+/* Indexes of CMOS control registers. */
+#define RTC_REG_A	0x0a    /* Register A: update-in-progress. */
+#define RTC_REG_B	0x0b    /* Register B: 24/12 hour time, irq enables. */
+#define RTC_REG_C	0x0c    /* Register C: pending interrupts. */
+#define RTC_REG_D	0x0d    /* Register D: valid time? */
+
+/* Register A. */
+#define RTCSA_UIP	0x80	/* Set while time update in progress. */
+
+/* Register B. */
+#define	RTCSB_SET	0x80	/* Disables update to let time be set. */
+#define RTCSB_DM	0x04	/* 0 = BCD time format, 1 = binary format. */
+#define RTCSB_24HR	0x02    /* 0 = 12-hour format, 1 = 24-hour format. */
+
+static int bcd_to_bin (uint8_t);
+static uint8_t cmos_read (uint8_t index);
+
+/* Returns number of seconds since Unix epoch of January 1,
+   1970. */
+time_t
+rtc_get_time (void)
+{
+  static const int days_per_month[12] =
+    {
+      31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+    };
+  int sec, min, hour, mday, mon, year;
+  time_t time;
+  int i;
+
+  /* Get time components.
+
+     We repeatedly read the time until it is stable from one read
+     to another, in case we start our initial read in the middle
+     of an update.  This strategy is not recommended by the
+     MC146818A datasheet, but it is simpler than any of their
+     suggestions and, furthermore, it is also used by Linux.
+
+     The MC146818A can be configured for BCD or binary format,
+     but for historical reasons everyone always uses BCD format
+     except on obscure non-PC platforms, so we don't bother
+     trying to detect the format in use. */
+  do
+    {
+      sec = bcd_to_bin (cmos_read (RTC_REG_SEC));
+      min = bcd_to_bin (cmos_read (RTC_REG_MIN));
+      hour = bcd_to_bin (cmos_read (RTC_REG_HOUR));
+      mday = bcd_to_bin (cmos_read (RTC_REG_MDAY));
+      mon = bcd_to_bin (cmos_read (RTC_REG_MON));
+      year = bcd_to_bin (cmos_read (RTC_REG_YEAR));
+    }
+  while (sec != bcd_to_bin (cmos_read (RTC_REG_SEC)));
+
+  /* Translate years-since-1900 into years-since-1970.
+     If it's before the epoch, assume that it has passed 2000.
+     This will break at 2070, but that's long after our 31-bit
+     time_t breaks in 2038. */
+  if (year < 70)
+    year += 100;
+  year -= 70;
+
+  /* Break down all components into seconds. */
+  time = (year * 365 + DIV_ROUND_UP (year - 2, 4)) * 24 * 60 * 60;
+  for (i = 1; i < mon; i++)
+    time += days_per_month[i - 1] * 24 * 60 * 60;
+  if (mon > 2 && year % 4 == 2)
+    time += 24 * 60 * 60;
+  time += (mday - 1) * 24 * 60 * 60;
+  time += hour * 60 * 60;
+  time += min * 60;
+  time += sec;
+
+  return time;
+}
+
+/* Returns the integer value of the given BCD byte. */
+static int
+bcd_to_bin (uint8_t x)
+{
+  return (x & 0x0f) + ((x >> 4) * 10);
+}
+
+/* Reads a byte from the CMOS register with the given INDEX and
+   returns the byte read. */
+static uint8_t
+cmos_read (uint8_t index)
+{
+  outb (CMOS_REG_SET, index);
+  return inb (CMOS_REG_IO);
+}
diff --git a/src/devices/rtc.h b/src/devices/rtc.h
new file mode 100644
index 0000000..96a822f
--- /dev/null
+++ b/src/devices/rtc.h
@@ -0,0 +1,8 @@
+#ifndef RTC_H
+#define RTC_H
+
+typedef unsigned long time_t;
+
+time_t rtc_get_time (void);
+
+#endif
diff --git a/src/devices/serial.c b/src/devices/serial.c
new file mode 100644
index 0000000..df770a7
--- /dev/null
+++ b/src/devices/serial.c
@@ -0,0 +1,228 @@
+#include "devices/serial.h"
+#include <debug.h>
+#include "devices/input.h"
+#include "devices/intq.h"
+#include "devices/timer.h"
+#include "threads/io.h"
+#include "threads/interrupt.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+/* Register definitions for the 16550A UART used in PCs.
+   The 16550A has a lot more going on than shown here, but this
+   is all we need.
+
+   Refer to [PC16650D] for hardware information. */
+
+/* I/O port base address for the first serial port. */
+#define IO_BASE 0x3f8
+
+/* DLAB=0 registers. */
+#define RBR_REG (IO_BASE + 0)   /* Receiver Buffer Reg. (read-only). */
+#define THR_REG (IO_BASE + 0)   /* Transmitter Holding Reg. (write-only). */
+#define IER_REG (IO_BASE + 1)   /* Interrupt Enable Reg.. */
+
+/* DLAB=1 registers. */
+#define LS_REG (IO_BASE + 0)    /* Divisor Latch (LSB). */
+#define MS_REG (IO_BASE + 1)    /* Divisor Latch (MSB). */
+
+/* DLAB-insensitive registers. */
+#define IIR_REG (IO_BASE + 2)   /* Interrupt Identification Reg. (read-only) */
+#define FCR_REG (IO_BASE + 2)   /* FIFO Control Reg. (write-only). */
+#define LCR_REG (IO_BASE + 3)   /* Line Control Register. */
+#define MCR_REG (IO_BASE + 4)   /* MODEM Control Register. */
+#define LSR_REG (IO_BASE + 5)   /* Line Status Register (read-only). */
+
+/* Interrupt Enable Register bits. */
+#define IER_RECV 0x01           /* Interrupt when data received. */
+#define IER_XMIT 0x02           /* Interrupt when transmit finishes. */
+
+/* Line Control Register bits. */
+#define LCR_N81 0x03            /* No parity, 8 data bits, 1 stop bit. */
+#define LCR_DLAB 0x80           /* Divisor Latch Access Bit (DLAB). */
+
+/* MODEM Control Register. */
+#define MCR_OUT2 0x08           /* Output line 2. */
+
+/* Line Status Register. */
+#define LSR_DR 0x01             /* Data Ready: received data byte is in RBR. */
+#define LSR_THRE 0x20           /* THR Empty. */
+
+/* Transmission mode. */
+static enum { UNINIT, POLL, QUEUE } mode;
+
+/* Data to be transmitted. */
+static struct intq txq;
+
+static void set_serial (int bps);
+static void putc_poll (uint8_t);
+static void write_ier (void);
+static intr_handler_func serial_interrupt;
+
+/* Initializes the serial port device for polling mode.
+   Polling mode busy-waits for the serial port to become free
+   before writing to it.  It's slow, but until interrupts have
+   been initialized it's all we can do. */
+static void
+init_poll (void) 
+{
+  ASSERT (mode == UNINIT);
+  outb (IER_REG, 0);                    /* Turn off all interrupts. */
+  outb (FCR_REG, 0);                    /* Disable FIFO. */
+  set_serial (9600);                    /* 9.6 kbps, N-8-1. */
+  outb (MCR_REG, MCR_OUT2);             /* Required to enable interrupts. */
+  intq_init (&txq);
+  mode = POLL;
+} 
+
+/* Initializes the serial port device for queued interrupt-driven
+   I/O.  With interrupt-driven I/O we don't waste CPU time
+   waiting for the serial device to become ready. */
+void
+serial_init_queue (void) 
+{
+  enum intr_level old_level;
+
+  if (mode == UNINIT)
+    init_poll ();
+  ASSERT (mode == POLL);
+
+  intr_register_ext (0x20 + 4, serial_interrupt, "serial");
+  mode = QUEUE;
+  old_level = intr_disable ();
+  write_ier ();
+  intr_set_level (old_level);
+}
+
+/* Sends BYTE to the serial port. */
+void
+serial_putc (uint8_t byte) 
+{
+  enum intr_level old_level = intr_disable ();
+
+  if (mode != QUEUE)
+    {
+      /* If we're not set up for interrupt-driven I/O yet,
+         use dumb polling to transmit a byte. */
+      if (mode == UNINIT)
+        init_poll ();
+      putc_poll (byte); 
+    }
+  else 
+    {
+      /* Otherwise, queue a byte and update the interrupt enable
+         register. */
+      if (old_level == INTR_OFF && intq_full (&txq)) 
+        {
+          /* Interrupts are off and the transmit queue is full.
+             If we wanted to wait for the queue to empty,
+             we'd have to reenable interrupts.
+             That's impolite, so we'll send a character via
+             polling instead. */
+          putc_poll (intq_getc (&txq)); 
+        }
+
+      intq_putc (&txq, byte); 
+      write_ier ();
+    }
+  
+  intr_set_level (old_level);
+}
+
+/* Flushes anything in the serial buffer out the port in polling
+   mode. */
+void
+serial_flush (void) 
+{
+  enum intr_level old_level = intr_disable ();
+  while (!intq_empty (&txq))
+    putc_poll (intq_getc (&txq));
+  intr_set_level (old_level);
+}
+
+/* The fullness of the input buffer may have changed.  Reassess
+   whether we should block receive interrupts.
+   Called by the input buffer routines when characters are added
+   to or removed from the buffer. */
+void
+serial_notify (void) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+  if (mode == QUEUE)
+    write_ier ();
+}
+
+/* Configures the serial port for BPS bits per second. */
+static void
+set_serial (int bps)
+{
+  int base_rate = 1843200 / 16;         /* Base rate of 16550A, in Hz. */
+  uint16_t divisor = base_rate / bps;   /* Clock rate divisor. */
+
+  ASSERT (bps >= 300 && bps <= 115200);
+
+  /* Enable DLAB. */
+  outb (LCR_REG, LCR_N81 | LCR_DLAB);
+
+  /* Set data rate. */
+  outb (LS_REG, divisor & 0xff);
+  outb (MS_REG, divisor >> 8);
+  
+  /* Reset DLAB. */
+  outb (LCR_REG, LCR_N81);
+}
+
+/* Update interrupt enable register. */
+static void
+write_ier (void) 
+{
+  uint8_t ier = 0;
+
+  ASSERT (intr_get_level () == INTR_OFF);
+
+  /* Enable transmit interrupt if we have any characters to
+     transmit. */
+  if (!intq_empty (&txq))
+    ier |= IER_XMIT;
+
+  /* Enable receive interrupt if we have room to store any
+     characters we receive. */
+  if (!input_full ())
+    ier |= IER_RECV;
+  
+  outb (IER_REG, ier);
+}
+
+/* Polls the serial port until it's ready,
+   and then transmits BYTE. */
+static void
+putc_poll (uint8_t byte) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+
+  while ((inb (LSR_REG) & LSR_THRE) == 0)
+    continue;
+  outb (THR_REG, byte);
+}
+
+/* Serial interrupt handler. */
+static void
+serial_interrupt (struct intr_frame *f UNUSED) 
+{
+  /* Inquire about interrupt in UART.  Without this, we can
+     occasionally miss an interrupt running under QEMU. */
+  inb (IIR_REG);
+
+  /* As long as we have room to receive a byte, and the hardware
+     has a byte for us, receive a byte.  */
+  while (!input_full () && (inb (LSR_REG) & LSR_DR) != 0)
+    input_putc (inb (RBR_REG));
+
+  /* As long as we have a byte to transmit, and the hardware is
+     ready to accept a byte for transmission, transmit a byte. */
+  while (!intq_empty (&txq) && (inb (LSR_REG) & LSR_THRE) != 0) 
+    outb (THR_REG, intq_getc (&txq));
+
+  /* Update interrupt enable register based on queue status. */
+  write_ier ();
+}
diff --git a/src/devices/serial.h b/src/devices/serial.h
new file mode 100644
index 0000000..6e04778
--- /dev/null
+++ b/src/devices/serial.h
@@ -0,0 +1,11 @@
+#ifndef DEVICES_SERIAL_H
+#define DEVICES_SERIAL_H
+
+#include <stdint.h>
+
+void serial_init_queue (void);
+void serial_putc (uint8_t);
+void serial_flush (void);
+void serial_notify (void);
+
+#endif /* devices/serial.h */
diff --git a/src/devices/shutdown.c b/src/devices/shutdown.c
new file mode 100644
index 0000000..da94c52
--- /dev/null
+++ b/src/devices/shutdown.c
@@ -0,0 +1,141 @@
+#include "devices/shutdown.h"
+#include <console.h>
+#include <stdio.h>
+#include "devices/kbd.h"
+#include "devices/serial.h"
+#include "devices/timer.h"
+#include "threads/io.h"
+#include "threads/thread.h"
+#ifdef USERPROG
+#include "userprog/exception.h"
+#endif
+#ifdef FILESYS
+#include "devices/block.h"
+#include "filesys/filesys.h"
+#endif
+
+/* Keyboard control register port. */
+#define CONTROL_REG 0x64
+
+/* How to shut down when shutdown() is called. */
+static enum shutdown_type how = SHUTDOWN_NONE;
+
+static void print_stats (void);
+
+/* Shuts down the machine in the way configured by
+   shutdown_configure().  If the shutdown type is SHUTDOWN_NONE
+   (which is the default), returns without doing anything. */
+void
+shutdown (void)
+{
+  switch (how)
+    {
+    case SHUTDOWN_POWER_OFF:
+      shutdown_power_off ();
+      break;
+
+    case SHUTDOWN_REBOOT:
+      shutdown_reboot ();
+      break;
+
+    default:
+      /* Nothing to do. */
+      break;
+    }
+}
+
+/* Sets TYPE as the way that machine will shut down when Pintos
+   execution is complete. */
+void
+shutdown_configure (enum shutdown_type type)
+{
+  how = type;
+}
+
+/* Reboots the machine via the keyboard controller. */
+void
+shutdown_reboot (void)
+{
+  printf ("Rebooting...\n");
+
+    /* See [kbd] for details on how to program the keyboard
+     * controller. */
+  for (;;)
+    {
+      int i;
+
+      /* Poll keyboard controller's status byte until
+       * 'input buffer empty' is reported. */
+      for (i = 0; i < 0x10000; i++)
+        {
+          if ((inb (CONTROL_REG) & 0x02) == 0)
+            break;
+          timer_udelay (2);
+        }
+
+      timer_udelay (50);
+
+      /* Pulse bit 0 of the output port P2 of the keyboard controller.
+       * This will reset the CPU. */
+      outb (CONTROL_REG, 0xfe);
+      timer_udelay (50);
+    }
+}
+
+/* Powers down the machine we're running on,
+   as long as we're running on Bochs or QEMU. */
+void
+shutdown_power_off (void)
+{
+  const char s[] = "Shutdown";
+  const char *p;
+
+#ifdef FILESYS
+  filesys_done ();
+#endif
+
+  print_stats ();
+
+  printf ("Powering off...\n");
+  serial_flush ();
+
+  /* ACPI power-off */
+  outw (0xB004, 0x2000);
+
+  /* This is a special power-off sequence supported by Bochs and
+     QEMU, but not by physical hardware. */
+  for (p = s; *p != '\0'; p++)
+    outb (0x8900, *p);
+
+  /* For newer versions of qemu, you must run with -device
+   * isa-debug-exit, which exits on any write to an IO port (by
+   * default 0x501).  Qemu's exit code is double the value plus one,
+   * so there is no way to exit cleanly.  We use 0x31 which should
+   * result in a qemu exit code of 0x63.  */
+  outb (0x501, 0x31);
+
+  /* This will power off a VMware VM if "gui.exitOnCLIHLT = TRUE"
+     is set in its configuration file.  (The "pintos" script does
+     that automatically.)  */
+  asm volatile ("cli; hlt" : : : "memory");
+
+  /* None of those worked. */
+  printf ("still running...\n");
+  for (;;);
+}
+
+/* Print statistics about Pintos execution. */
+static void
+print_stats (void)
+{
+  timer_print_stats ();
+  thread_print_stats ();
+#ifdef FILESYS
+  block_print_stats ();
+#endif
+  console_print_stats ();
+  kbd_print_stats ();
+#ifdef USERPROG
+  exception_print_stats ();
+#endif
+}
diff --git a/src/devices/shutdown.h b/src/devices/shutdown.h
new file mode 100644
index 0000000..dc4f942
--- /dev/null
+++ b/src/devices/shutdown.h
@@ -0,0 +1,19 @@
+#ifndef DEVICES_SHUTDOWN_H
+#define DEVICES_SHUTDOWN_H
+
+#include <debug.h>
+
+/* How to shut down when Pintos has nothing left to do. */
+enum shutdown_type
+  {
+    SHUTDOWN_NONE,              /* Loop forever. */
+    SHUTDOWN_POWER_OFF,         /* Power off the machine (if possible). */
+    SHUTDOWN_REBOOT,            /* Reboot the machine (if possible). */
+  };
+
+void shutdown (void);
+void shutdown_configure (enum shutdown_type);
+void shutdown_reboot (void) NO_RETURN;
+void shutdown_power_off (void) NO_RETURN;
+
+#endif /* devices/shutdown.h */
diff --git a/src/devices/speaker.c b/src/devices/speaker.c
new file mode 100644
index 0000000..5052005
--- /dev/null
+++ b/src/devices/speaker.c
@@ -0,0 +1,68 @@
+#include "devices/speaker.h"
+#include "devices/pit.h"
+#include "threads/io.h"
+#include "threads/interrupt.h"
+#include "devices/timer.h"
+
+/* Speaker port enable I/O register. */
+#define SPEAKER_PORT_GATE	0x61
+
+/* Speaker port enable bits. */
+#define SPEAKER_GATE_ENABLE	0x03
+
+/* Sets the PC speaker to emit a tone at the given FREQUENCY, in
+   Hz. */
+void
+speaker_on (int frequency)
+{
+  if (frequency >= 20 && frequency <= 20000)
+    {
+      /* Set the timer channel that's connected to the speaker to
+         output a square wave at the given FREQUENCY, then
+         connect the timer channel output to the speaker. */
+      enum intr_level old_level = intr_disable ();
+      pit_configure_channel (2, 3, frequency);
+      outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) | SPEAKER_GATE_ENABLE);
+      intr_set_level (old_level);
+    }
+  else
+    {
+      /* FREQUENCY is outside the range of normal human hearing.
+         Just turn off the speaker. */
+      speaker_off ();
+    }
+}
+
+/* Turn off the PC speaker, by disconnecting the timer channel's
+   output from the speaker. */
+void
+speaker_off (void)
+{
+  enum intr_level old_level = intr_disable ();
+  outb (SPEAKER_PORT_GATE, inb (SPEAKER_PORT_GATE) & ~SPEAKER_GATE_ENABLE);
+  intr_set_level (old_level);
+}
+
+/* Briefly beep the PC speaker. */
+void
+speaker_beep (void)
+{
+  /* Only attempt to beep the speaker if interrupts are enabled,
+     because we don't want to freeze the machine during the beep.
+     We could add a hook to the timer interrupt to avoid that
+     problem, but then we'd risk failing to ever stop the beep if
+     Pintos crashes for some unrelated reason.  There's nothing
+     more annoying than a machine whose beeping you can't stop
+     without a power cycle.
+
+     We can't just enable interrupts while we sleep.  For one
+     thing, we get called (indirectly) from printf, which should
+     always work, even during boot before we're ready to enable
+     interrupts. */
+  if (intr_get_level () == INTR_ON)
+    {
+      speaker_on (440);
+      timer_msleep (250);
+      speaker_off ();
+    }
+}
diff --git a/src/devices/speaker.h b/src/devices/speaker.h
new file mode 100644
index 0000000..98cef7b
--- /dev/null
+++ b/src/devices/speaker.h
@@ -0,0 +1,8 @@
+#ifndef DEVICES_SPEAKER_H
+#define DEVICES_SPEAKER_H
+
+void speaker_on (int frequency);
+void speaker_off (void);
+void speaker_beep (void);
+
+#endif /* devices/speaker.h */
diff --git a/src/devices/timer.c b/src/devices/timer.c
new file mode 100644
index 0000000..8b92341
--- /dev/null
+++ b/src/devices/timer.c
@@ -0,0 +1,246 @@
+#include "devices/timer.h"
+#include <debug.h>
+#include <inttypes.h>
+#include <round.h>
+#include <stdio.h>
+#include "devices/pit.h"
+#include "threads/interrupt.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+  
+/* See [8254] for hardware details of the 8254 timer chip. */
+
+#if TIMER_FREQ < 19
+#error 8254 timer requires TIMER_FREQ >= 19
+#endif
+#if TIMER_FREQ > 1000
+#error TIMER_FREQ <= 1000 recommended
+#endif
+
+/* Number of timer ticks since OS booted. */
+static int64_t ticks;
+
+/* Number of loops per timer tick.
+   Initialized by timer_calibrate(). */
+static unsigned loops_per_tick;
+
+static intr_handler_func timer_interrupt;
+static bool too_many_loops (unsigned loops);
+static void busy_wait (int64_t loops);
+static void real_time_sleep (int64_t num, int32_t denom);
+static void real_time_delay (int64_t num, int32_t denom);
+
+/* Sets up the timer to interrupt TIMER_FREQ times per second,
+   and registers the corresponding interrupt. */
+void
+timer_init (void) 
+{
+  pit_configure_channel (0, 2, TIMER_FREQ);
+  intr_register_ext (0x20, timer_interrupt, "8254 Timer");
+}
+
+/* Calibrates loops_per_tick, used to implement brief delays. */
+void
+timer_calibrate (void) 
+{
+  unsigned high_bit, test_bit;
+
+  ASSERT (intr_get_level () == INTR_ON);
+  printf ("Calibrating timer...  ");
+
+  /* Approximate loops_per_tick as the largest power-of-two
+     still less than one timer tick. */
+  loops_per_tick = 1u << 10;
+  while (!too_many_loops (loops_per_tick << 1)) 
+    {
+      loops_per_tick <<= 1;
+      ASSERT (loops_per_tick != 0);
+    }
+
+  /* Refine the next 8 bits of loops_per_tick. */
+  high_bit = loops_per_tick;
+  for (test_bit = high_bit >> 1; test_bit != high_bit >> 10; test_bit >>= 1)
+    if (!too_many_loops (loops_per_tick | test_bit))
+      loops_per_tick |= test_bit;
+
+  printf ("%'"PRIu64" loops/s.\n", (uint64_t) loops_per_tick * TIMER_FREQ);
+}
+
+/* Returns the number of timer ticks since the OS booted. */
+int64_t
+timer_ticks (void) 
+{
+  enum intr_level old_level = intr_disable ();
+  int64_t t = ticks;
+  intr_set_level (old_level);
+  return t;
+}
+
+/* Returns the number of timer ticks elapsed since THEN, which
+   should be a value once returned by timer_ticks(). */
+int64_t
+timer_elapsed (int64_t then) 
+{
+  return timer_ticks () - then;
+}
+
+/* Sleeps for approximately TICKS timer ticks.  Interrupts must
+   be turned on. */
+void
+timer_sleep (int64_t ticks) 
+{
+  int64_t start = timer_ticks ();
+
+  ASSERT (intr_get_level () == INTR_ON);
+  while (timer_elapsed (start) < ticks) 
+    thread_yield ();
+}
+
+/* Sleeps for approximately MS milliseconds.  Interrupts must be
+   turned on. */
+void
+timer_msleep (int64_t ms) 
+{
+  real_time_sleep (ms, 1000);
+}
+
+/* Sleeps for approximately US microseconds.  Interrupts must be
+   turned on. */
+void
+timer_usleep (int64_t us) 
+{
+  real_time_sleep (us, 1000 * 1000);
+}
+
+/* Sleeps for approximately NS nanoseconds.  Interrupts must be
+   turned on. */
+void
+timer_nsleep (int64_t ns) 
+{
+  real_time_sleep (ns, 1000 * 1000 * 1000);
+}
+
+/* Busy-waits for approximately MS milliseconds.  Interrupts need
+   not be turned on.
+
+   Busy waiting wastes CPU cycles, and busy waiting with
+   interrupts off for the interval between timer ticks or longer
+   will cause timer ticks to be lost.  Thus, use timer_msleep()
+   instead if interrupts are enabled. */
+void
+timer_mdelay (int64_t ms) 
+{
+  real_time_delay (ms, 1000);
+}
+
+/* Sleeps for approximately US microseconds.  Interrupts need not
+   be turned on.
+
+   Busy waiting wastes CPU cycles, and busy waiting with
+   interrupts off for the interval between timer ticks or longer
+   will cause timer ticks to be lost.  Thus, use timer_usleep()
+   instead if interrupts are enabled. */
+void
+timer_udelay (int64_t us) 
+{
+  real_time_delay (us, 1000 * 1000);
+}
+
+/* Sleeps execution for approximately NS nanoseconds.  Interrupts
+   need not be turned on.
+
+   Busy waiting wastes CPU cycles, and busy waiting with
+   interrupts off for the interval between timer ticks or longer
+   will cause timer ticks to be lost.  Thus, use timer_nsleep()
+   instead if interrupts are enabled.*/
+void
+timer_ndelay (int64_t ns) 
+{
+  real_time_delay (ns, 1000 * 1000 * 1000);
+}
+
+/* Prints timer statistics. */
+void
+timer_print_stats (void) 
+{
+  printf ("Timer: %"PRId64" ticks\n", timer_ticks ());
+}
+
+/* Timer interrupt handler. */
+static void
+timer_interrupt (struct intr_frame *args UNUSED)
+{
+  ticks++;
+  thread_tick ();
+}
+
+/* Returns true if LOOPS iterations waits for more than one timer
+   tick, otherwise false. */
+static bool
+too_many_loops (unsigned loops) 
+{
+  /* Wait for a timer tick. */
+  int64_t start = ticks;
+  while (ticks == start)
+    barrier ();
+
+  /* Run LOOPS loops. */
+  start = ticks;
+  busy_wait (loops);
+
+  /* If the tick count changed, we iterated too long. */
+  barrier ();
+  return start != ticks;
+}
+
+/* Iterates through a simple loop LOOPS times, for implementing
+   brief delays.
+
+   Marked NO_INLINE because code alignment can significantly
+   affect timings, so that if this function was inlined
+   differently in different places the results would be difficult
+   to predict. */
+static void NO_INLINE
+busy_wait (int64_t loops) 
+{
+  while (loops-- > 0)
+    barrier ();
+}
+
+/* Sleep for approximately NUM/DENOM seconds. */
+static void
+real_time_sleep (int64_t num, int32_t denom) 
+{
+  /* Convert NUM/DENOM seconds into timer ticks, rounding down.
+          
+        (NUM / DENOM) s          
+     ---------------------- = NUM * TIMER_FREQ / DENOM ticks. 
+     1 s / TIMER_FREQ ticks
+  */
+  int64_t ticks = num * TIMER_FREQ / denom;
+
+  ASSERT (intr_get_level () == INTR_ON);
+  if (ticks > 0)
+    {
+      /* We're waiting for at least one full timer tick.  Use
+         timer_sleep() because it will yield the CPU to other
+         processes. */                
+      timer_sleep (ticks); 
+    }
+  else 
+    {
+      /* Otherwise, use a busy-wait loop for more accurate
+         sub-tick timing. */
+      real_time_delay (num, denom); 
+    }
+}
+
+/* Busy-wait for approximately NUM/DENOM seconds. */
+static void
+real_time_delay (int64_t num, int32_t denom)
+{
+  /* Scale the numerator and denominator down by 1000 to avoid
+     the possibility of overflow. */
+  ASSERT (denom % 1000 == 0);
+  busy_wait (loops_per_tick * num / 1000 * TIMER_FREQ / (denom / 1000)); 
+}
diff --git a/src/devices/timer.h b/src/devices/timer.h
new file mode 100644
index 0000000..cd3d6bb
--- /dev/null
+++ b/src/devices/timer.h
@@ -0,0 +1,29 @@
+#ifndef DEVICES_TIMER_H
+#define DEVICES_TIMER_H
+
+#include <round.h>
+#include <stdint.h>
+
+/* Number of timer interrupts per second. */
+#define TIMER_FREQ 100
+
+void timer_init (void);
+void timer_calibrate (void);
+
+int64_t timer_ticks (void);
+int64_t timer_elapsed (int64_t);
+
+/* Sleep and yield the CPU to other threads. */
+void timer_sleep (int64_t ticks);
+void timer_msleep (int64_t milliseconds);
+void timer_usleep (int64_t microseconds);
+void timer_nsleep (int64_t nanoseconds);
+
+/* Busy waits. */
+void timer_mdelay (int64_t milliseconds);
+void timer_udelay (int64_t microseconds);
+void timer_ndelay (int64_t nanoseconds);
+
+void timer_print_stats (void);
+
+#endif /* devices/timer.h */
diff --git a/src/devices/vga.c b/src/devices/vga.c
new file mode 100644
index 0000000..f421b61
--- /dev/null
+++ b/src/devices/vga.c
@@ -0,0 +1,172 @@
+#include "devices/vga.h"
+#include <round.h>
+#include <stdint.h>
+#include <stddef.h>
+#include <string.h>
+#include "devices/speaker.h"
+#include "threads/io.h"
+#include "threads/interrupt.h"
+#include "threads/vaddr.h"
+
+/* VGA text screen support.  See [FREEVGA] for more information. */
+
+/* Number of columns and rows on the text display. */
+#define COL_CNT 80
+#define ROW_CNT 25
+
+/* Current cursor position.  (0,0) is in the upper left corner of
+   the display. */
+static size_t cx, cy;
+
+/* Attribute value for gray text on a black background. */
+#define GRAY_ON_BLACK 0x07
+
+/* Framebuffer.  See [FREEVGA] under "VGA Text Mode Operation".
+   The character at (x,y) is fb[y][x][0].
+   The attribute at (x,y) is fb[y][x][1]. */
+static uint8_t (*fb)[COL_CNT][2];
+
+static void clear_row (size_t y);
+static void cls (void);
+static void newline (void);
+static void move_cursor (void);
+static void find_cursor (size_t *x, size_t *y);
+
+/* Initializes the VGA text display. */
+static void
+init (void)
+{
+  /* Already initialized? */
+  static bool inited;
+  if (!inited)
+    {
+      fb = ptov (0xb8000);
+      find_cursor (&cx, &cy);
+      inited = true; 
+    }
+}
+
+/* Writes C to the VGA text display, interpreting control
+   characters in the conventional ways.  */
+void
+vga_putc (int c)
+{
+  /* Disable interrupts to lock out interrupt handlers
+     that might write to the console. */
+  enum intr_level old_level = intr_disable ();
+
+  init ();
+  
+  switch (c) 
+    {
+    case '\n':
+      newline ();
+      break;
+
+    case '\f':
+      cls ();
+      break;
+
+    case '\b':
+      if (cx > 0)
+        cx--;
+      break;
+      
+    case '\r':
+      cx = 0;
+      break;
+
+    case '\t':
+      cx = ROUND_UP (cx + 1, 8);
+      if (cx >= COL_CNT)
+        newline ();
+      break;
+
+    case '\a':
+      intr_set_level (old_level);
+      speaker_beep ();
+      intr_disable ();
+      break;
+      
+    default:
+      fb[cy][cx][0] = c;
+      fb[cy][cx][1] = GRAY_ON_BLACK;
+      if (++cx >= COL_CNT)
+        newline ();
+      break;
+    }
+
+  /* Update cursor position. */
+  move_cursor ();
+
+  intr_set_level (old_level);
+}
+
+/* Clears the screen and moves the cursor to the upper left. */
+static void
+cls (void)
+{
+  size_t y;
+
+  for (y = 0; y < ROW_CNT; y++)
+    clear_row (y);
+
+  cx = cy = 0;
+  move_cursor ();
+}
+
+/* Clears row Y to spaces. */
+static void
+clear_row (size_t y) 
+{
+  size_t x;
+
+  for (x = 0; x < COL_CNT; x++)
+    {
+      fb[y][x][0] = ' ';
+      fb[y][x][1] = GRAY_ON_BLACK;
+    }
+}
+
+/* Advances the cursor to the first column in the next line on
+   the screen.  If the cursor is already on the last line on the
+   screen, scrolls the screen upward one line. */
+static void
+newline (void)
+{
+  cx = 0;
+  cy++;
+  if (cy >= ROW_CNT)
+    {
+      cy = ROW_CNT - 1;
+      memmove (&fb[0], &fb[1], sizeof fb[0] * (ROW_CNT - 1));
+      clear_row (ROW_CNT - 1);
+    }
+}
+
+/* Moves the hardware cursor to (cx,cy). */
+static void
+move_cursor (void) 
+{
+  /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
+  uint16_t cp = cx + COL_CNT * cy;
+  outw (0x3d4, 0x0e | (cp & 0xff00));
+  outw (0x3d4, 0x0f | (cp << 8));
+}
+
+/* Reads the current hardware cursor position into (*X,*Y). */
+static void
+find_cursor (size_t *x, size_t *y) 
+{
+  /* See [FREEVGA] under "Manipulating the Text-mode Cursor". */
+  uint16_t cp;
+
+  outb (0x3d4, 0x0e);
+  cp = inb (0x3d5) << 8;
+
+  outb (0x3d4, 0x0f);
+  cp |= inb (0x3d5);
+
+  *x = cp % COL_CNT;
+  *y = cp / COL_CNT;
+}
diff --git a/src/devices/vga.h b/src/devices/vga.h
new file mode 100644
index 0000000..59690fb
--- /dev/null
+++ b/src/devices/vga.h
@@ -0,0 +1,6 @@
+#ifndef DEVICES_VGA_H
+#define DEVICES_VGA_H
+
+void vga_putc (int);
+
+#endif /* devices/vga.h */
diff --git a/src/examples/.gitignore b/src/examples/.gitignore
new file mode 100644
index 0000000..a9e09d7
--- /dev/null
+++ b/src/examples/.gitignore
@@ -0,0 +1,19 @@
+cat
+cmp
+cp
+echo
+halt
+hex-dump
+ls
+mcat
+mcp
+mkdir
+pwd
+rm
+shell
+bubsort
+insult
+lineup
+matmult
+recursor
+*.d
diff --git a/src/examples/Makefile b/src/examples/Makefile
new file mode 100644
index 0000000..be94f7a
--- /dev/null
+++ b/src/examples/Makefile
@@ -0,0 +1,33 @@
+SRCDIR = ..
+
+# Test programs to compile, and a list of sources for each.
+# To add a new test, put its name on the PROGS list
+# and then add a name_SRC line that lists its source files.
+PROGS = cat cmp cp echo halt hex-dump ls mcat mcp mkdir pwd rm shell \
+	bubsort lineup matmult recursor
+
+# Should work from project 2 onward.
+cat_SRC = cat.c
+cmp_SRC = cmp.c
+cp_SRC = cp.c
+echo_SRC = echo.c
+halt_SRC = halt.c
+hex-dump_SRC = hex-dump.c
+lineup_SRC = lineup.c
+ls_SRC = ls.c
+recursor_SRC = recursor.c
+rm_SRC = rm.c
+
+# Should work in project 3; also in project 4 if VM is included.
+bubsort_SRC = bubsort.c
+matmult_SRC = matmult.c
+mcat_SRC = mcat.c
+mcp_SRC = mcp.c
+
+# Should work in project 4.
+mkdir_SRC = mkdir.c
+pwd_SRC = pwd.c
+shell_SRC = shell.c
+
+include $(SRCDIR)/Make.config
+include $(SRCDIR)/Makefile.userprog
diff --git a/src/examples/bubsort.c b/src/examples/bubsort.c
new file mode 100644
index 0000000..343219e
--- /dev/null
+++ b/src/examples/bubsort.c
@@ -0,0 +1,38 @@
+/* sort.c 
+
+   Test program to sort a large number of integers.
+ 
+   Intention is to stress virtual memory system.
+ 
+   Ideally, we could read the unsorted array off of the file
+   system, and store the result back to the file system! */
+#include <stdio.h>
+
+/* Size of array to sort. */
+#define SORT_SIZE 128
+
+int
+main (void)
+{
+  /* Array to sort.  Static to reduce stack usage. */
+  static int array[SORT_SIZE];
+
+  int i, j, tmp;
+
+  /* First initialize the array in descending order. */
+  for (i = 0; i < SORT_SIZE; i++)
+    array[i] = SORT_SIZE - i - 1;
+
+  /* Then sort in ascending order. */
+  for (i = 0; i < SORT_SIZE - 1; i++)
+    for (j = 0; j < SORT_SIZE - 1 - i; j++)
+      if (array[j] > array[j + 1])
+	{
+	  tmp = array[j];
+	  array[j] = array[j + 1];
+	  array[j + 1] = tmp;
+	}
+
+  printf ("sort exiting with code %d\n", array[0]);
+  return array[0];
+}
diff --git a/src/examples/cat.c b/src/examples/cat.c
new file mode 100644
index 0000000..c8d229d
--- /dev/null
+++ b/src/examples/cat.c
@@ -0,0 +1,34 @@
+/* cat.c
+
+   Prints files specified on command line to the console. */
+
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  bool success = true;
+  int i;
+  
+  for (i = 1; i < argc; i++) 
+    {
+      int fd = open (argv[i]);
+      if (fd < 0) 
+        {
+          printf ("%s: open failed\n", argv[i]);
+          success = false;
+          continue;
+        }
+      for (;;) 
+        {
+          char buffer[1024];
+          int bytes_read = read (fd, buffer, sizeof buffer);
+          if (bytes_read == 0)
+            break;
+          write (STDOUT_FILENO, buffer, bytes_read);
+        }
+      close (fd);
+    }
+  return success ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/examples/cmp.c b/src/examples/cmp.c
new file mode 100644
index 0000000..94b406d
--- /dev/null
+++ b/src/examples/cmp.c
@@ -0,0 +1,68 @@
+/* cat.c
+
+   Compares two files. */
+
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  int fd[2];
+
+  if (argc != 3) 
+    {
+      printf ("usage: cmp A B\n");
+      return EXIT_FAILURE;
+    }
+
+  /* Open files. */
+  fd[0] = open (argv[1]);
+  if (fd[0] < 0) 
+    {
+      printf ("%s: open failed\n", argv[1]);
+      return EXIT_FAILURE;
+    }
+  fd[1] = open (argv[2]);
+  if (fd[1] < 0) 
+    {
+      printf ("%s: open failed\n", argv[1]);
+      return EXIT_FAILURE;
+    }
+
+  /* Compare data. */
+  for (;;) 
+    {
+      int pos;
+      char buffer[2][1024];
+      int bytes_read[2];
+      int min_read;
+      int i;
+
+      pos = tell (fd[0]);
+      bytes_read[0] = read (fd[0], buffer[0], sizeof buffer[0]);
+      bytes_read[1] = read (fd[1], buffer[1], sizeof buffer[1]);
+      min_read = bytes_read[0] < bytes_read[1] ? bytes_read[0] : bytes_read[1];
+      if (min_read == 0)
+        break;
+
+      for (i = 0; i < min_read; i++)
+        if (buffer[0][i] != buffer[1][i]) 
+          {
+            printf ("Byte %d is %02hhx ('%c') in %s but %02hhx ('%c') in %s\n",
+                    pos + i,
+                    buffer[0][i], buffer[0][i], argv[1],
+                    buffer[1][i], buffer[1][i], argv[2]);
+            return EXIT_FAILURE;
+          }
+
+      if (min_read < bytes_read[1])
+        printf ("%s is shorter than %s\n", argv[1], argv[2]);
+      else if (min_read < bytes_read[0])
+        printf ("%s is shorter than %s\n", argv[2], argv[1]);
+    }
+
+  printf ("%s and %s are identical\n", argv[1], argv[2]);
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/examples/cp.c b/src/examples/cp.c
new file mode 100644
index 0000000..86a5cd7
--- /dev/null
+++ b/src/examples/cp.c
@@ -0,0 +1,55 @@
+/* cat.c
+
+Copies one file to another. */
+
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  int in_fd, out_fd;
+
+  if (argc != 3) 
+    {
+      printf ("usage: cp OLD NEW\n");
+      return EXIT_FAILURE;
+    }
+
+  /* Open input file. */
+  in_fd = open (argv[1]);
+  if (in_fd < 0) 
+    {
+      printf ("%s: open failed\n", argv[1]);
+      return EXIT_FAILURE;
+    }
+
+  /* Create and open output file. */
+  if (!create (argv[2], filesize (in_fd))) 
+    {
+      printf ("%s: create failed\n", argv[2]);
+      return EXIT_FAILURE;
+    }
+  out_fd = open (argv[2]);
+  if (out_fd < 0) 
+    {
+      printf ("%s: open failed\n", argv[2]);
+      return EXIT_FAILURE;
+    }
+
+  /* Copy data. */
+  for (;;) 
+    {
+      char buffer[1024];
+      int bytes_read = read (in_fd, buffer, sizeof buffer);
+      if (bytes_read == 0)
+        break;
+      if (write (out_fd, buffer, bytes_read) != bytes_read) 
+        {
+          printf ("%s: write failed\n", argv[2]);
+          return EXIT_FAILURE;
+        }
+    }
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/examples/echo.c b/src/examples/echo.c
new file mode 100644
index 0000000..1b136f2
--- /dev/null
+++ b/src/examples/echo.c
@@ -0,0 +1,14 @@
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (int argc, char **argv)
+{
+  int i;
+
+  for (i = 0; i < argc; i++)
+    printf ("%s ", argv[i]);
+  printf ("\n");
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/examples/halt.c b/src/examples/halt.c
new file mode 100644
index 0000000..bad7250
--- /dev/null
+++ b/src/examples/halt.c
@@ -0,0 +1,14 @@
+/* halt.c
+
+   Simple program to test whether running a user program works.
+ 	
+   Just invokes a system call that shuts down the OS. */
+
+#include <syscall.h>
+
+int
+main (void)
+{
+  halt ();
+  /* not reached */
+}
diff --git a/src/examples/hex-dump.c b/src/examples/hex-dump.c
new file mode 100644
index 0000000..ee313f2
--- /dev/null
+++ b/src/examples/hex-dump.c
@@ -0,0 +1,35 @@
+/* hex-dump.c
+
+   Prints files specified on command line to the console in hex. */
+
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  bool success = true;
+  int i;
+  
+  for (i = 1; i < argc; i++) 
+    {
+      int fd = open (argv[i]);
+      if (fd < 0) 
+        {
+          printf ("%s: open failed\n", argv[i]);
+          success = false;
+          continue;
+        }
+      for (;;) 
+        {
+          char buffer[1024];
+          int pos = tell (fd);
+          int bytes_read = read (fd, buffer, sizeof buffer);
+          if (bytes_read == 0)
+            break;
+          hex_dump (pos, buffer, bytes_read, true);
+        }
+      close (fd);
+    }
+  return success ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/examples/lib/.gitignore b/src/examples/lib/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/src/examples/lib/.gitignore
@@ -0,0 +1 @@
+*.d
diff --git a/src/examples/lib/user/.dummy b/src/examples/lib/user/.dummy
new file mode 100644
index 0000000..e69de29
diff --git a/src/examples/lib/user/.gitignore b/src/examples/lib/user/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/src/examples/lib/user/.gitignore
@@ -0,0 +1 @@
+*.d
diff --git a/src/examples/lineup.c b/src/examples/lineup.c
new file mode 100644
index 0000000..60402d0
--- /dev/null
+++ b/src/examples/lineup.c
@@ -0,0 +1,46 @@
+/* lineup.c
+
+   Converts a file to uppercase in-place.
+
+   Incidentally, another way to do this while avoiding the seeks
+   would be to open the input file, then remove() it and reopen
+   it under another handle.  Because of Unix deletion semantics
+   this works fine. */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (int argc, char *argv[])
+{
+  char buf[1024];
+  int handle;
+
+  if (argc != 2)
+    exit (1);
+
+  handle = open (argv[1]);
+  if (handle < 0)
+    exit (2);
+
+  for (;;) 
+    {
+      int n, i;
+
+      n = read (handle, buf, sizeof buf);
+      if (n <= 0)
+        break;
+
+      for (i = 0; i < n; i++)
+        buf[i] = toupper ((unsigned char) buf[i]);
+
+      seek (handle, tell (handle) - n);
+      if (write (handle, buf, n) != n)
+        printf ("write failed\n");
+    }
+
+  close (handle);
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/examples/ls.c b/src/examples/ls.c
new file mode 100644
index 0000000..fbe27a1
--- /dev/null
+++ b/src/examples/ls.c
@@ -0,0 +1,90 @@
+/* ls.c
+  
+   Lists the contents of the directory or directories named on
+   the command line, or of the current directory if none are
+   named.
+
+   By default, only the name of each file is printed.  If "-l" is
+   given as the first argument, the type, size, and inumber of
+   each file is also printed.  This won't work until project 4. */
+
+#include <syscall.h>
+#include <stdio.h>
+#include <string.h>
+
+static bool
+list_dir (const char *dir, bool verbose) 
+{
+  int dir_fd = open (dir);
+  if (dir_fd == -1) 
+    {
+      printf ("%s: not found\n", dir);
+      return false;
+    }
+
+  if (isdir (dir_fd))
+    {
+      char name[READDIR_MAX_LEN];
+
+      printf ("%s", dir);
+      if (verbose)
+        printf (" (inumber %d)", inumber (dir_fd));
+      printf (":\n");
+
+      while (readdir (dir_fd, name)) 
+        {
+          printf ("%s", name); 
+          if (verbose) 
+            {
+              char full_name[128];
+              int entry_fd;
+
+              snprintf (full_name, sizeof full_name, "%s/%s", dir, name);
+              entry_fd = open (full_name);
+
+              printf (": ");
+              if (entry_fd != -1)
+                {
+                  if (isdir (entry_fd))
+                    printf ("directory");
+                  else
+                    printf ("%d-byte file", filesize (entry_fd));
+                  printf (", inumber %d", inumber (entry_fd));
+                }
+              else
+                printf ("open failed");
+              close (entry_fd);
+            }
+          printf ("\n");
+        }
+    }
+  else 
+    printf ("%s: not a directory\n", dir);
+  close (dir_fd);
+  return true;
+}
+
+int
+main (int argc, char *argv[]) 
+{
+  bool success = true;
+  bool verbose = false;
+  
+  if (argc > 1 && !strcmp (argv[1], "-l")) 
+    {
+      verbose = true;
+      argv++;
+      argc--;
+    }
+  
+  if (argc <= 1)
+    success = list_dir (".", verbose);
+  else 
+    {
+      int i;
+      for (i = 1; i < argc; i++)
+        if (!list_dir (argv[i], verbose))
+          success = false;
+    }
+  return success ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/examples/matmult.c b/src/examples/matmult.c
new file mode 100644
index 0000000..4f0615f
--- /dev/null
+++ b/src/examples/matmult.c
@@ -0,0 +1,57 @@
+/* matmult.c 
+
+   Test program to do matrix multiplication on large arrays.
+ 
+   Intended to stress virtual memory system.
+   
+   Ideally, we could read the matrices off of the file system,
+   and store the result back to the file system!
+ */
+
+#include <stdio.h>
+#include <syscall.h>
+
+/* You should define DIM to be large enough that the arrays
+   don't fit in physical memory.
+
+    Dim       Memory
+ ------     --------
+     16         3 kB
+     64        48 kB
+    128       192 kB
+    256       768 kB
+    512     3,072 kB
+  1,024    12,288 kB
+  2,048    49,152 kB
+  4,096   196,608 kB
+  8,192   786,432 kB
+ 16,384 3,145,728 kB */
+#define DIM 128
+
+int A[DIM][DIM];
+int B[DIM][DIM];
+int C[DIM][DIM];
+
+int
+main (void)
+{
+  int i, j, k;
+
+  /* Initialize the matrices. */
+  for (i = 0; i < DIM; i++)
+    for (j = 0; j < DIM; j++)
+      {
+	A[i][j] = i;
+	B[i][j] = j;
+	C[i][j] = 0;
+      }
+
+  /* Multiply matrices. */
+  for (i = 0; i < DIM; i++)	
+    for (j = 0; j < DIM; j++)
+      for (k = 0; k < DIM; k++)
+	C[i][j] += A[i][k] * B[k][j];
+
+  /* Done. */
+  exit (C[DIM - 1][DIM - 1]);
+}
diff --git a/src/examples/mcat.c b/src/examples/mcat.c
new file mode 100644
index 0000000..7b39760
--- /dev/null
+++ b/src/examples/mcat.c
@@ -0,0 +1,45 @@
+/* mcat.c
+
+   Prints files specified on command line to the console, using
+   mmap. */
+
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  int i;
+  
+  for (i = 1; i < argc; i++) 
+    {
+      int fd;
+      mapid_t map;
+      void *data = (void *) 0x10000000;
+      int size;
+
+      /* Open input file. */
+      fd = open (argv[i]);
+      if (fd < 0) 
+        {
+          printf ("%s: open failed\n", argv[i]);
+          return EXIT_FAILURE;
+        }
+      size = filesize (fd);
+
+      /* Map files. */
+      map = mmap (fd, data);
+      if (map == MAP_FAILED) 
+        {
+          printf ("%s: mmap failed\n", argv[i]);
+          return EXIT_FAILURE;
+        }
+
+      /* Write file to console. */
+      write (STDOUT_FILENO, data, size);
+
+      /* Unmap files (optional). */
+      munmap (map);
+    }
+  return EXIT_SUCCESS;
+}
diff --git a/src/examples/mcp.c b/src/examples/mcp.c
new file mode 100644
index 0000000..6091dc8
--- /dev/null
+++ b/src/examples/mcp.c
@@ -0,0 +1,68 @@
+/* mcp.c
+
+   Copies one file to another, using mmap. */
+
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  int in_fd, out_fd;
+  mapid_t in_map, out_map;
+  void *in_data = (void *) 0x10000000;
+  void *out_data = (void *) 0x20000000;
+  int size;
+
+  if (argc != 3) 
+    {
+      printf ("usage: cp OLD NEW\n");
+      return EXIT_FAILURE;
+    }
+
+  /* Open input file. */
+  in_fd = open (argv[1]);
+  if (in_fd < 0) 
+    {
+      printf ("%s: open failed\n", argv[1]);
+      return EXIT_FAILURE;
+    }
+  size = filesize (in_fd);
+
+  /* Create and open output file. */
+  if (!create (argv[2], size)) 
+    {
+      printf ("%s: create failed\n", argv[2]);
+      return EXIT_FAILURE;
+    }
+  out_fd = open (argv[2]);
+  if (out_fd < 0) 
+    {
+      printf ("%s: open failed\n", argv[2]);
+      return EXIT_FAILURE;
+    }
+
+  /* Map files. */
+  in_map = mmap (in_fd, in_data);
+  if (in_map == MAP_FAILED) 
+    {
+      printf ("%s: mmap failed\n", argv[1]);
+      return EXIT_FAILURE;
+    }
+  out_map = mmap (out_fd, out_data);
+  if (out_map == MAP_FAILED)
+    {
+      printf ("%s: mmap failed\n", argv[2]);
+      return EXIT_FAILURE;
+    }
+
+  /* Copy files. */
+  memcpy (out_data, in_data, size);
+
+  /* Unmap files (optional). */
+  munmap (in_map);
+  munmap (out_map);
+
+  return EXIT_SUCCESS;
+}
diff --git a/src/examples/mkdir.c b/src/examples/mkdir.c
new file mode 100644
index 0000000..7ddbc3f
--- /dev/null
+++ b/src/examples/mkdir.c
@@ -0,0 +1,24 @@
+/* mkdir.c
+
+   Creates a directory. */
+
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  if (argc != 2) 
+    {
+      printf ("usage: %s DIRECTORY\n", argv[0]);
+      return EXIT_FAILURE;
+    }
+
+  if (!mkdir (argv[1])) 
+    {
+      printf ("%s: mkdir failed\n", argv[1]);
+      return EXIT_FAILURE;
+    }
+  
+  return EXIT_SUCCESS;
+}
diff --git a/src/examples/pwd.c b/src/examples/pwd.c
new file mode 100644
index 0000000..d2305cf
--- /dev/null
+++ b/src/examples/pwd.c
@@ -0,0 +1,152 @@
+/* pwd.c
+   
+   Prints the absolute name of the present working directory. */
+
+#include <syscall.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+static bool getcwd (char *cwd, size_t cwd_size);
+
+int
+main (void) 
+{
+  char cwd[128];
+  if (getcwd (cwd, sizeof cwd)) 
+    {
+      printf ("%s\n", cwd);
+      return EXIT_SUCCESS;
+    }
+  else 
+    {
+      printf ("error\n");
+      return EXIT_FAILURE; 
+    }
+}
+
+/* Stores the inode number for FILE_NAME in *INUM.
+   Returns true if successful, false if the file could not be
+   opened. */
+static bool
+get_inumber (const char *file_name, int *inum) 
+{
+  int fd = open (file_name);
+  if (fd >= 0) 
+    {
+      *inum = inumber (fd);
+      close (fd);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Prepends PREFIX to the characters stored in the final *DST_LEN
+   bytes of the DST_SIZE-byte buffer that starts at DST.
+   Returns true if successful, false if adding that many
+   characters, plus a null terminator, would overflow the buffer.
+   (No null terminator is actually added or depended upon, but
+   its space is accounted for.) */
+static bool
+prepend (const char *prefix,
+         char *dst, size_t *dst_len, size_t dst_size) 
+{
+  size_t prefix_len = strlen (prefix);
+  if (prefix_len + *dst_len + 1 <= dst_size) 
+    {
+      *dst_len += prefix_len;
+      memcpy ((dst + dst_size) - *dst_len, prefix, prefix_len);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Stores the current working directory, as a null-terminated
+   string, in the CWD_SIZE bytes in CWD.
+   Returns true if successful, false on error.  Errors include
+   system errors, directory trees deeper than MAX_LEVEL levels,
+   and insufficient space in CWD. */
+static bool
+getcwd (char *cwd, size_t cwd_size) 
+{
+  size_t cwd_len = 0;   
+  
+#define MAX_LEVEL 20
+  char name[MAX_LEVEL * 3 + 1 + READDIR_MAX_LEN + 1];
+  char *namep;
+
+  int child_inum;
+
+  /* Make sure there's enough space for at least "/". */
+  if (cwd_size < 2)
+    return false;
+
+  /* Get inumber for current directory. */
+  if (!get_inumber (".", &child_inum))
+    return false;
+
+  namep = name;
+  for (;;)
+    {
+      int parent_inum, parent_fd;
+
+      /* Compose "../../../..", etc., in NAME. */
+      if ((namep - name) > MAX_LEVEL * 3)
+        return false;
+      *namep++ = '.';
+      *namep++ = '.';
+      *namep = '\0';
+
+      /* Open directory. */
+      parent_fd = open (name);
+      if (parent_fd < 0)
+        return false;
+      *namep++ = '/';
+
+      /* If parent and child have the same inumber,
+         then we've arrived at the root. */
+      parent_inum = inumber (parent_fd);
+      if (parent_inum == child_inum)
+        break;
+
+      /* Find name of file in parent directory with the child's
+         inumber. */
+      for (;;)
+        {
+          int test_inum;
+          if (!readdir (parent_fd, namep) || !get_inumber (name, &test_inum)) 
+            {
+              close (parent_fd);
+              return false; 
+            }
+          if (test_inum == child_inum)
+            break;
+        }
+      close (parent_fd);
+
+      /* Prepend "/name" to CWD. */
+      if (!prepend (namep - 1, cwd, &cwd_len, cwd_size))
+        return false;
+
+      /* Move up. */
+      child_inum = parent_inum;
+    }
+
+  /* Finalize CWD. */
+  if (cwd_len > 0) 
+    {
+      /* Move the string to the beginning of CWD,
+         and null-terminate it. */
+      memmove (cwd, (cwd + cwd_size) - cwd_len, cwd_len);
+      cwd[cwd_len] = '\0';
+    }
+  else 
+    {
+      /* Special case for the root. */
+      strlcpy (cwd, "/", cwd_size); 
+    }
+  
+  return true;
+}
diff --git a/src/examples/recursor.c b/src/examples/recursor.c
new file mode 100644
index 0000000..79c784a
--- /dev/null
+++ b/src/examples/recursor.c
@@ -0,0 +1,34 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <syscall.h>
+
+int
+main (int argc, char *argv[])
+{
+  char buffer[128];
+  pid_t pid;
+  int retval = 0;
+
+  if (argc != 4) 
+    {
+      printf ("usage: recursor <string> <depth> <waitp>\n");
+      exit (1);
+    }
+
+  /* Print args. */
+  printf ("%s %s %s %s\n", argv[0], argv[1], argv[2], argv[3]);
+
+  /* Execute child and wait for it to finish if requested. */
+  if (atoi (argv[2]) != 0) 
+    {
+      snprintf (buffer, sizeof buffer,
+                "recursor %s %d %s", argv[1], atoi (argv[2]) - 1, argv[3]);
+      pid = exec (buffer);
+      if (atoi (argv[3]))
+        retval = wait (pid);
+    }
+  
+  /* Done. */
+  printf ("%s %s: dying, retval=%d\n", argv[1], argv[2], retval);
+  exit (retval);
+}
diff --git a/src/examples/rm.c b/src/examples/rm.c
new file mode 100644
index 0000000..0db7f7b
--- /dev/null
+++ b/src/examples/rm.c
@@ -0,0 +1,21 @@
+/* rm.c
+
+   Removes files specified on command line. */
+
+#include <stdio.h>
+#include <syscall.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  bool success = true;
+  int i;
+  
+  for (i = 1; i < argc; i++)
+    if (!remove (argv[i])) 
+      {
+        printf ("%s: remove failed\n", argv[i]);
+        success = false; 
+      }
+  return success ? EXIT_SUCCESS : EXIT_FAILURE;
+}
diff --git a/src/examples/shell.c b/src/examples/shell.c
new file mode 100644
index 0000000..93641b4
--- /dev/null
+++ b/src/examples/shell.c
@@ -0,0 +1,104 @@
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+
+static void read_line (char line[], size_t);
+static bool backspace (char **pos, char line[]);
+
+int
+main (void)
+{
+  printf ("Shell starting...\n");
+  for (;;) 
+    {
+      char command[80];
+
+      /* Read command. */
+      printf ("--");
+      read_line (command, sizeof command);
+      
+      /* Execute command. */
+      if (!strcmp (command, "exit"))
+        break;
+      else if (!memcmp (command, "cd ", 3)) 
+        {
+          if (!chdir (command + 3))
+            printf ("\"%s\": chdir failed\n", command + 3);
+        }
+      else if (command[0] == '\0') 
+        {
+          /* Empty command. */
+        }
+      else
+        {
+          pid_t pid = exec (command);
+          if (pid != PID_ERROR)
+            printf ("\"%s\": exit code %d\n", command, wait (pid));
+          else
+            printf ("exec failed\n");
+        }
+    }
+
+  printf ("Shell exiting.");
+  return EXIT_SUCCESS;
+}
+
+/* Reads a line of input from the user into LINE, which has room
+   for SIZE bytes.  Handles backspace and Ctrl+U in the ways
+   expected by Unix users.  On return, LINE will always be
+   null-terminated and will not end in a new-line character. */
+static void
+read_line (char line[], size_t size) 
+{
+  char *pos = line;
+  for (;;)
+    {
+      char c;
+      read (STDIN_FILENO, &c, 1);
+
+      switch (c) 
+        {
+        case '\r':
+          *pos = '\0';
+          putchar ('\n');
+          return;
+
+        case '\b':
+          backspace (&pos, line);
+          break;
+
+        case ('U' - 'A') + 1:       /* Ctrl+U. */
+          while (backspace (&pos, line))
+            continue;
+          break;
+
+        default:
+          /* Add character to line. */
+          if (pos < line + size - 1) 
+            {
+              putchar (c);
+              *pos++ = c;
+            }
+          break;
+        }
+    }
+}
+
+/* If *POS is past the beginning of LINE, backs up one character
+   position.  Returns true if successful, false if nothing was
+   done. */
+static bool
+backspace (char **pos, char line[]) 
+{
+  if (*pos > line)
+    {
+      /* Back up cursor, overwrite character, back up
+         again. */
+      printf ("\b \b");
+      (*pos)--;
+      return true;
+    }
+  else
+    return false;
+}
diff --git a/src/filesys/.gitignore b/src/filesys/.gitignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/src/filesys/.gitignore
@@ -0,0 +1,3 @@
+build
+bochsrc.txt
+bochsout.txt
diff --git a/src/filesys/Make.vars b/src/filesys/Make.vars
new file mode 100644
index 0000000..d04ab67
--- /dev/null
+++ b/src/filesys/Make.vars
@@ -0,0 +1,13 @@
+# -*- makefile -*-
+
+kernel.bin: DEFINES = -DUSERPROG -DFILESYS
+KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys
+TEST_SUBDIRS = tests/userprog tests/filesys/base tests/filesys/extended
+GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.no-vm
+SIMULATOR = --qemu
+
+# Uncomment the lines below to enable VM.
+kernel.bin: DEFINES += -DVM
+KERNEL_SUBDIRS += vm
+TEST_SUBDIRS += tests/vm
+GRADING_FILE = $(SRCDIR)/tests/filesys/Grading.with-vm
diff --git a/src/filesys/Makefile b/src/filesys/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/src/filesys/Makefile
@@ -0,0 +1 @@
+include ../Makefile.kernel
diff --git a/src/filesys/cache.c b/src/filesys/cache.c
new file mode 100644
index 0000000..d982f01
--- /dev/null
+++ b/src/filesys/cache.c
@@ -0,0 +1,149 @@
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+#include <lib/stdbool.h>
+#include <string.h>
+#include <lib/stdio.h>
+#include <devices/timer.h>
+#include "cache.h"
+#include "filesys.h"
+#include "../threads/synch.h"
+
+#define CACHE_SIZE 64
+
+struct cache_block
+  {
+    unsigned char buf[BLOCK_SECTOR_SIZE];
+    block_sector_t sector_idx;
+    int accessed;
+    bool dirty;
+    bool used;
+  };
+
+int cache_get_free_cache (void);
+
+static struct cache_block cache[CACHE_SIZE];
+//struct lock locks_cache[CACHE_SIZE];
+static struct lock lock_cache_all;
+
+int cnt_used, current_cache;
+
+int cache_get_free_cache ()
+{
+  if (cnt_used == 0)
+    {
+      current_cache = 1;
+      return 0;
+    }
+  while (cache[current_cache].used && cache[current_cache].accessed)
+    {
+      //lock_acquire (&locks_cache[current_cache]);
+      cache[current_cache].accessed = 0;
+      //lock_release (&locks_cache[current_cache]);
+      current_cache = (current_cache + 1) % CACHE_SIZE;
+    }
+  //lock_acquire (&locks_cache[current_cache]);
+  if (cache[current_cache].used && cache[current_cache].dirty)
+    {
+      block_write (fs_device, cache[current_cache].sector_idx, cache[current_cache].buf);
+    }
+  if (cache[current_cache].used)
+    {
+      cache[current_cache].used = false;
+      --cnt_used;
+    }
+  //lock_release (&locks_cache[current_cache]);
+  int ret = current_cache;
+  current_cache = (current_cache + 1) % CACHE_SIZE;
+  return ret;
+}
+
+void cache_init ()
+{
+  lock_init (&lock_cache_all);
+  cnt_used = 0;
+  current_cache = -1;
+  for (int i = 0; i < CACHE_SIZE; ++i)
+    {
+      cache[i].dirty = cache[i].used = false;
+      cache[i].accessed = 0;
+      memset (cache[i].buf, 0, BLOCK_SECTOR_SIZE);
+      //lock_init (&locks_cache[i]);
+    }
+}
+
+void cache_read (block_sector_t sector, void *buffer)
+{
+  lock_acquire (&lock_cache_all);
+  bool found = false;
+  for (int i = 0; i < CACHE_SIZE; ++i)
+    if (cache[i].used && cache[i].sector_idx == sector)
+      {
+        //lock_acquire (&locks_cache[i]);
+        memcpy (buffer, cache[i].buf, BLOCK_SECTOR_SIZE);
+        ++cache[i].accessed;
+        //lock_release (&locks_cache[i]);
+        found = true;
+        lock_release (&lock_cache_all);
+        break;
+      }
+  if (found)
+    return;
+  int index = cache_get_free_cache ();
+  ++cnt_used;
+  //lock_acquire (&locks_cache[index]);
+  cache[index].sector_idx = sector;
+  cache[index].dirty = false;
+  cache[index].used = true;
+  cache[index].accessed = 1;
+  block_read (fs_device, sector, cache[index].buf);
+  memcpy (buffer, cache[index].buf, BLOCK_SECTOR_SIZE);
+  //lock_release (&locks_cache[index]);
+  lock_release (&lock_cache_all);
+}
+
+void cache_write (block_sector_t sector, const void *buffer)
+{
+  lock_acquire (&lock_cache_all);
+  bool found = false;
+  for (int i = 0; i < CACHE_SIZE; ++i)
+    if (cache[i].used && cache[i].sector_idx == sector)
+      {
+        //lock_acquire (&locks_cache[i]);
+        memcpy (cache[i].buf, buffer, BLOCK_SECTOR_SIZE);
+        cache[i].dirty = true;
+        ++cache[i].accessed;
+        //lock_release (&locks_cache[i]);
+        found = true;
+        lock_release (&lock_cache_all);
+        break;
+      }
+  if (found)
+    return;
+  int index = cache_get_free_cache ();
+  ++cnt_used;
+  //lock_acquire (&locks_cache[index]);
+  cache[index].sector_idx = sector;
+  cache[index].dirty = true;
+  cache[index].used = true;
+  cache[index].accessed = 1;
+  memcpy(cache[index].buf, buffer, BLOCK_SECTOR_SIZE);
+  //lock_release (&locks_cache[index]);
+  lock_release (&lock_cache_all);
+}
+
+void cache_done ()
+{
+  lock_acquire (&lock_cache_all);
+  for (int i = 0; i < CACHE_SIZE; ++i)
+    if (cache[i].used && cache[i].dirty)
+      {
+        //lock_acquire (&locks_cache[i]);
+        block_write (fs_device, cache[i].sector_idx, cache[i].buf);
+        cache[i].used = false;
+        //lock_release (&locks_cache[i]);
+        --cnt_used;
+      }
+  lock_release (&lock_cache_all);
+}
+
+/* ----- Newly Add for Proj04 FileSys ----- */
diff --git a/src/filesys/cache.h b/src/filesys/cache.h
new file mode 100644
index 0000000..f09bfe0
--- /dev/null
+++ b/src/filesys/cache.h
@@ -0,0 +1,15 @@
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+#ifndef FILESYS_CACHE_H
+#define FILESYS_CACHE_H
+
+#include "devices/block.h"
+
+void cache_init (void);
+void cache_read (block_sector_t sector, void *buffer);
+void cache_write (block_sector_t sector, const void *buffer);
+void cache_done (void);
+
+#endif /* filesys/cache.h */
+
+/* ----- Newly Add for Proj04 FileSys ----- */
\ No newline at end of file
diff --git a/src/filesys/directory.c b/src/filesys/directory.c
new file mode 100644
index 0000000..35d088e
--- /dev/null
+++ b/src/filesys/directory.c
@@ -0,0 +1,440 @@
+#include "filesys/directory.h"
+#include <stdio.h>
+#include <string.h>
+#include <list.h>
+#include "filesys/filesys.h"
+#include "filesys/inode.h"
+#include "threads/malloc.h"
+#include "filesys/free-map.h"
+#include "filesys/file.h"
+
+#define DIR_BASE_ENTRY 2
+
+/* A single directory entry. */
+struct dir_entry 
+{
+  block_sector_t inode_sector;        /* Sector number of header. */
+  char name[NAME_MAX + 1];            /* Null terminated file name. */
+  bool in_use;                        /* In use or free? */
+};
+
+/* Creates a directory with space for ENTRY_CNT entries in the
+   given SECTOR.  Returns true if successful, false on failure. */
+bool
+dir_create (block_sector_t sector, size_t entry_cnt)
+{
+  bool success = inode_create (sector, (DIR_BASE_ENTRY + entry_cnt) * sizeof (struct dir_entry));
+
+  if(success)
+    {
+      struct inode* inode;
+      struct dir *dir;
+	  
+	  inode = inode_open (sector);
+      ASSERT (inode != NULL);
+      inode_set_dir (inode);
+      
+      dir = dir_open (inode);
+      ASSERT (dir != NULL);
+      ASSERT (dir_add(dir, ".", sector));
+      ASSERT (dir_add(dir, "..", sector));
+      dir_close(dir);
+    }
+    
+  return success;
+}
+
+/* Opens and returns the directory for the given INODE, of which
+   it takes ownership.  Returns a null pointer on failure. */
+struct dir *
+dir_open (struct inode *inode) 
+{
+  struct dir *dir = calloc (1, sizeof *dir);
+  if (inode != NULL && dir != NULL)
+    {
+      /* ----- Newly Add for Proj04 FileSys ----- */
+      ASSERT (inode_isdir(inode));
+      dir->inode = inode;
+      dir->pos = DIR_BASE_ENTRY * sizeof(struct dir_entry);
+      /* ----- Newly Add for Proj04 FileSys ----- */
+      return dir;
+    }
+  else
+    {
+      inode_close (inode);
+      free (dir);
+      return NULL; 
+    }
+}
+
+/* Opens the root directory and returns a directory for it.
+   Return true if successful, false on failure. */
+struct dir *
+dir_open_root (void)
+{
+  return dir_open (inode_open (ROOT_DIR_SECTOR));
+}
+
+/* Opens and returns a new directory for the same inode as DIR.
+   Returns a null pointer on failure. */
+struct dir *
+dir_reopen (struct dir *dir) 
+{
+  return dir_open (inode_reopen (dir->inode));
+}
+
+/* Destroys DIR and frees associated resources. */
+void
+dir_close (struct dir *dir) 
+{
+  if (dir != NULL)
+    {
+      inode_close (dir->inode);
+      free (dir);
+    }
+}
+
+/* Returns the inode encapsulated by DIR. */
+struct inode *
+dir_get_inode (struct dir *dir) 
+{
+  return dir->inode;
+}
+
+/* Searches DIR for a file with the given NAME.
+   If successful, returns true, sets *EP to the directory entry
+   if EP is non-null, and sets *OFSP to the byte offset of the
+   directory entry if OFSP is non-null.
+   otherwise, returns false and ignores EP and OFSP. */
+static bool
+lookup (const struct dir *dir, const char *name,
+        struct dir_entry *ep, off_t *ofsp) 
+{
+  struct dir_entry e;
+  size_t ofs;
+  
+  ASSERT (dir != NULL);
+  ASSERT (name != NULL);
+
+  for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
+       ofs += sizeof e) 
+    if (e.in_use && !strcmp (name, e.name)) 
+      {
+        if (ep != NULL)
+          *ep = e;
+        if (ofsp != NULL)
+          *ofsp = ofs;
+        return true;
+      }
+  return false;
+}
+
+/* Searches DIR for a file with the given NAME
+   and returns true if one exists, false otherwise.
+   On success, sets *INODE to an inode for the file, otherwise to
+   a null pointer.  The caller must close *INODE. */
+bool
+dir_lookup (const struct dir *dir, const char *name,
+            struct inode **inode) 
+{
+  struct dir_entry e;
+
+  ASSERT (dir != NULL);
+  ASSERT (name != NULL);
+
+  if (lookup (dir, name, &e, NULL))
+    *inode = inode_open (e.inode_sector);
+  else
+    *inode = NULL;
+
+  return *inode != NULL;
+}
+
+/* Adds a file named NAME to DIR, which must not already contain a
+   file by that name.  The file's inode is in sector
+   INODE_SECTOR.
+   Returns true if successful, false on failure.
+   Fails if NAME is invalid (i.e. too long) or a disk or memory
+   error occurs. */
+bool
+dir_add (struct dir *dir, const char *name, block_sector_t inode_sector)
+{
+  struct dir_entry e;
+  off_t ofs;
+  bool success = false;
+
+  ASSERT (dir != NULL);
+  ASSERT (name != NULL);
+
+  /* Check NAME for validity. */
+  if (*name == '\0' || strlen (name) > NAME_MAX)
+    return false;
+
+  /* Check that NAME is not in use. */
+  if (lookup (dir, name, NULL, NULL))
+    goto done;
+
+  /* Set OFS to offset of free slot.
+     If there are no free slots, then it will be set to the
+     current end-of-file.
+     
+     inode_read_at() will only return a short read at end of file.
+     Otherwise, we'd need to verify that we didn't get a short
+     read due to something intermittent such as low memory. */
+  for (ofs = 0; inode_read_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
+       ofs += sizeof e) 
+    if (!e.in_use)
+      break;
+
+  /* Write slot. */
+  e.in_use = true;
+  strlcpy (e.name, name, sizeof e.name);
+  e.inode_sector = inode_sector;
+  success = inode_write_at (dir->inode, &e, sizeof e, ofs) == sizeof e;
+
+  /* ----- Newly Add for Proj04 FileSys ----- */
+  if (success && inode_sector != inode_get_inumber(dir->inode))
+    {  
+      struct inode* inode;
+      inode = inode_open (inode_sector);
+      ASSERT (inode != NULL);
+  
+      if(inode_isdir (inode))
+        {
+          struct dir *subdir = dir_open (inode);
+          ASSERT (lookup (subdir, "..", &e, &ofs));
+          e.inode_sector = inode_get_inumber(dir->inode);
+          ASSERT (inode_write_at(subdir->inode, &e, sizeof e, ofs) == sizeof e);
+          dir_close (subdir);
+        }
+      else
+        {
+          inode_close (inode);
+        }
+    }
+    /* ----- Newly Add for Proj04 FileSys ----- */
+
+ done:
+  return success;
+}
+
+/* Removes any entry for NAME in DIR.
+   Returns true if successful, false on failure,
+   which occurs only if there is no file with the given NAME. */
+bool
+dir_remove (struct dir *dir, const char *name) 
+{
+  struct dir_entry e;
+  struct inode *inode = NULL;
+  bool success = false;
+  off_t ofs;
+
+  ASSERT (dir != NULL);
+  ASSERT (name != NULL);
+
+  /* Find directory entry. */
+  if (!lookup (dir, name, &e, &ofs))
+    goto done;
+
+  /* Open inode. */
+  inode = inode_open (e.inode_sector);
+  if (inode == NULL)
+    goto done;
+
+  /* Erase directory entry. */
+  e.in_use = false;
+  if (inode_write_at (dir->inode, &e, sizeof e, ofs) != sizeof e) 
+    goto done;
+
+  /* Remove inode. */
+  inode_remove (inode);
+  success = true;
+
+ done:
+  inode_close (inode);
+  return success;
+}
+
+/* Reads the next directory entry in DIR and stores the name in
+   NAME.  Returns true if successful, false if the directory
+   contains no more entries. */
+bool
+dir_readdir (struct dir *dir, char name[NAME_MAX + 1])
+{
+  struct dir_entry e;
+
+  while (inode_read_at (dir->inode, &e, sizeof e, dir->pos) == sizeof e) 
+    {
+      dir->pos += sizeof e;
+      if (e.in_use)
+        {
+          strlcpy (name, e.name, NAME_MAX + 1);
+          return true;
+        } 
+    }
+  return false;
+}
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+bool
+subdir_create(struct dir* current_dir, char* subdir_name)
+{
+  ASSERT(current_dir != NULL);
+  ASSERT(subdir_name != NULL);
+  if (strlen(subdir_name) == 0)
+  {
+    return false;
+  }
+  block_sector_t block_sector = -1;
+  bool success = (current_dir != NULL
+                  && free_map_allocate(1, &block_sector)
+                  && dir_create(block_sector, 0)
+                  && dir_add(current_dir, subdir_name, block_sector));
+  if (!success && block_sector != -1)
+  {
+    free_map_release(block_sector, 1);
+  }
+  return success;
+}
+
+struct dir*
+subdir_lookup(struct dir* current_dir, char* subdir_name)
+{
+  ASSERT(current_dir != NULL)
+  ASSERT(subdir_name != NULL)
+  if (strlen(subdir_name) == 0)
+  {
+    return NULL;
+  }
+  struct inode* inode = NULL;
+  bool res = dir_lookup(current_dir, subdir_name, &inode);
+  if (!res || inode == NULL)
+  {
+    return NULL;
+  }
+  if (!inode_isdir(inode))
+  {
+    inode_close(inode);
+    return NULL;
+  }
+  return dir_open(inode);
+}
+
+bool
+subdir_delete(struct dir* current_dir, char* subdir_name)
+{
+  ASSERT(current_dir != NULL)
+  ASSERT(subdir_name != NULL)
+  if (strlen(subdir_name) == 0)
+  {
+    return false;
+  }
+  struct inode* inode = NULL;
+  bool res = dir_lookup(current_dir, subdir_name, &inode);
+  if (!res || inode == NULL)
+  {
+    return false;
+  }
+  if (!inode_isdir(inode))
+  {
+    inode_close(inode);
+    return false;
+  }
+  if (inode_get_inumber(inode) 
+      == inode_get_inumber(dir_get_inode(thread_current()->current_dir)))
+  {
+    inode_close(inode);
+    return false;
+  }
+  if (inode_get_opencnt(inode) > 1)
+  {
+    inode_close(inode);
+    return false;
+  }
+  struct dir* dir_copy = dir_open(inode);
+  char* buffer = malloc(NAME_MAX + 1);
+  ASSERT(buffer != NULL)
+  if (dir_readdir(dir_copy, buffer))
+  {
+    dir_close(dir_copy);
+    free(buffer);
+    return false;
+  }
+  dir_close(dir_copy);
+  free(buffer);
+  return dir_remove(current_dir, subdir_name);
+}
+
+bool
+subfile_create(struct dir* current_dir, char* file_name, off_t initial_size)
+{
+  ASSERT(file_name != NULL);
+  if (strlen(file_name) == 0)
+  {
+    return false;
+  }
+  block_sector_t block_sector = -1;
+  bool success = (current_dir != NULL
+                  && free_map_allocate(1, &block_sector)
+                  && inode_create(block_sector, initial_size)
+                  && dir_add(current_dir, file_name, block_sector));
+  if (!success && block_sector != -1)
+    free_map_release(block_sector, 1);
+  return success;
+}
+
+struct file*
+subfile_lookup(struct dir* current_dir, char* file_name)
+{
+  ASSERT(current_dir != NULL)
+  ASSERT(file_name != NULL)
+  if (strlen(file_name) == 0)
+  {
+    return NULL;
+  }
+  struct inode* inode = NULL;
+  bool res = dir_lookup(current_dir, file_name, &inode);
+  if (!res || inode == NULL)
+  {
+    return false;
+  }
+  if (inode_isdir(inode))
+  {
+    inode_close(inode);
+    return NULL;
+  }
+  struct file* file = file_open(inode);
+  set_file_dir(file, dir_reopen(current_dir));
+  return file;
+}
+
+bool
+subfile_delete(struct dir* current_dir, char* file_name)
+{
+  ASSERT(current_dir != NULL)
+  ASSERT(file_name != NULL)
+  if (strlen(file_name) == 0)
+  {
+    return false;
+  }
+  struct inode* inode = NULL;
+  bool res = dir_lookup(current_dir, file_name, &inode);
+  if (!res || inode == NULL)
+  {
+    return false;
+  }
+  if (inode_isdir(inode))
+  {
+    inode_close(inode);
+    return false;
+  }
+  inode_close(inode);
+  return dir_remove(current_dir, file_name);
+}
+
+bool
+is_dirfile(struct thread_file* fh)
+{
+  return inode_isdir(file_get_inode(fh->file));
+}
+/* ----- Newly Add for Proj04 FileSys ----- */
\ No newline at end of file
diff --git a/src/filesys/directory.h b/src/filesys/directory.h
new file mode 100644
index 0000000..b7e0c1f
--- /dev/null
+++ b/src/filesys/directory.h
@@ -0,0 +1,52 @@
+#ifndef FILESYS_DIRECTORY_H
+#define FILESYS_DIRECTORY_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "devices/block.h"
+#include "off_t.h"
+#include "threads/thread.h"
+
+/* Maximum length of a file name component.
+   This is the traditional UNIX maximum length.
+   After directories are implemented, this maximum length may be
+   retained, but much longer full path names must be allowed. */
+#define NAME_MAX 14
+
+struct inode;
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+struct dir
+{
+    struct inode *inode;                /* Backing store. */
+    off_t pos;                          /* Current position. */
+};
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+/* Opening and closing directories. */
+bool dir_create (block_sector_t sector, size_t entry_cnt);
+struct dir *dir_open (struct inode *);
+struct dir *dir_open_root (void);
+struct dir *dir_reopen (struct dir *);
+void dir_close (struct dir *);
+struct inode *dir_get_inode (struct dir *);
+
+/* Reading and writing. */
+bool dir_lookup (const struct dir *, const char *name, struct inode **);
+bool dir_add (struct dir *, const char *name, block_sector_t);
+bool dir_remove (struct dir *, const char *name);
+bool dir_readdir (struct dir *, char name[NAME_MAX + 1]);
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+bool subdir_create(struct dir* current_dir, char* subdir_name);
+struct dir* subdir_lookup(struct dir* current_dir, char* subdir_name);
+bool subdir_delete(struct dir* current_dir, char* subdir_name);
+
+bool subfile_create(struct dir* current_dir, char* file_name, off_t initial_size);
+struct file* subfile_lookup(struct dir* current_dir, char* file_name);
+bool subfile_delete(struct dir* current_dir, char* file_name);
+
+bool is_dirfile(struct thread_file* fh);
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+#endif /* filesys/directory.h */
diff --git a/src/filesys/file.c b/src/filesys/file.c
new file mode 100644
index 0000000..d296fc9
--- /dev/null
+++ b/src/filesys/file.c
@@ -0,0 +1,207 @@
+#include "filesys/file.h"
+#include <debug.h>
+#include "filesys/inode.h"
+#include "threads/malloc.h"
+
+#ifdef FILESYS
+#include "filesys/directory.h"
+#endif
+
+/* An open file. */
+struct file 
+  {
+    struct inode *inode;        /* File's inode. */
+    off_t pos;                  /* Current position. */
+    bool deny_write;            /* Has file_deny_write() been called? */
+
+    /* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+    struct dir* dir; // for process_load
+#endif
+    /* ----- Newly Add for Proj04 FileSys ----- */
+  };
+
+/* Opens a file for the given INODE, of which it takes ownership,
+   and returns the new file.  Returns a null pointer if an
+   allocation fails or if INODE is null. */
+struct file *
+file_open (struct inode *inode) 
+{
+  struct file *file = calloc (1, sizeof *file);
+  if (inode != NULL && file != NULL)
+    {
+      file->inode = inode;
+      file->pos = 0;
+      file->deny_write = false;
+      return file;
+    }
+  else
+    {
+      inode_close (inode);
+      free (file);
+      return NULL; 
+    }
+}
+
+/* Opens and returns a new file for the same inode as FILE.
+   Returns a null pointer if unsuccessful. */
+struct file *
+file_reopen (struct file *file) 
+{
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+  struct file* res = file_open (inode_reopen (file->inode));
+  set_file_dir(res, dir_reopen(file->dir));
+  return res;
+#else
+  return file_open (inode_reopen (file->inode));
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+}
+
+/* Closes FILE. */
+void
+file_close (struct file *file) 
+{
+  if (file != NULL)
+    {
+      file_allow_write (file);
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+      dir_close(file->dir);
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+      inode_close (file->inode);
+      free (file); 
+    }
+}
+
+/* Returns the inode encapsulated by FILE. */
+struct inode *
+file_get_inode (struct file *file) 
+{
+  return file->inode;
+}
+
+/* Reads SIZE bytes from FILE into BUFFER,
+   starting at the file's current position.
+   Returns the number of bytes actually read,
+   which may be less than SIZE if end of file is reached.
+   Advances FILE's position by the number of bytes read. */
+off_t
+file_read (struct file *file, void *buffer, off_t size) 
+{
+  off_t bytes_read = inode_read_at (file->inode, buffer, size, file->pos);
+  file->pos += bytes_read;
+  return bytes_read;
+}
+
+/* Reads SIZE bytes from FILE into BUFFER,
+   starting at offset FILE_OFS in the file.
+   Returns the number of bytes actually read,
+   which may be less than SIZE if end of file is reached.
+   The file's current position is unaffected. */
+off_t
+file_read_at (struct file *file, void *buffer, off_t size, off_t file_ofs) 
+{
+  return inode_read_at (file->inode, buffer, size, file_ofs);
+}
+
+/* Writes SIZE bytes from BUFFER into FILE,
+   starting at the file's current position.
+   Returns the number of bytes actually written,
+   which may be less than SIZE if end of file is reached.
+   (Normally we'd grow the file in that case, but file growth is
+   not yet implemented.)
+   Advances FILE's position by the number of bytes read. */
+off_t
+file_write (struct file *file, const void *buffer, off_t size) 
+{
+  off_t bytes_written = inode_write_at (file->inode, buffer, size, file->pos);
+  file->pos += bytes_written;
+  return bytes_written;
+}
+
+/* Writes SIZE bytes from BUFFER into FILE,
+   starting at offset FILE_OFS in the file.
+   Returns the number of bytes actually written,
+   which may be less than SIZE if end of file is reached.
+   (Normally we'd grow the file in that case, but file growth is
+   not yet implemented.)
+   The file's current position is unaffected. */
+off_t
+file_write_at (struct file *file, const void *buffer, off_t size,
+               off_t file_ofs) 
+{
+  return inode_write_at (file->inode, buffer, size, file_ofs);
+}
+
+/* Prevents write operations on FILE's underlying inode
+   until file_allow_write() is called or FILE is closed. */
+void
+file_deny_write (struct file *file) 
+{
+  ASSERT (file != NULL);
+  if (!file->deny_write) 
+    {
+      file->deny_write = true;
+      inode_deny_write (file->inode);
+    }
+}
+
+/* Re-enables write operations on FILE's underlying inode.
+   (Writes might still be denied by some other file that has the
+   same inode open.) */
+void
+file_allow_write (struct file *file) 
+{
+  ASSERT (file != NULL);
+  if (file->deny_write) 
+    {
+      file->deny_write = false;
+      inode_allow_write (file->inode);
+    }
+}
+
+/* Returns the size of FILE in bytes. */
+off_t
+file_length (struct file *file) 
+{
+  ASSERT (file != NULL);
+  return inode_length (file->inode);
+}
+
+/* Sets the current position in FILE to NEW_POS bytes from the
+   start of the file. */
+void
+file_seek (struct file *file, off_t new_pos)
+{
+  ASSERT (file != NULL);
+  ASSERT (new_pos >= 0);
+  file->pos = new_pos;
+}
+
+/* Returns the current position in FILE as a byte offset from the
+   start of the file. */
+off_t
+file_tell (struct file *file) 
+{
+  ASSERT (file != NULL);
+  return file->pos;
+}
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+void
+set_file_dir(struct file *file, struct dir* dir)
+{
+    file->dir = dir;
+}
+
+struct dir*
+get_file_dir(struct file* file)
+{
+  return file->dir;
+}
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
\ No newline at end of file
diff --git a/src/filesys/file.h b/src/filesys/file.h
new file mode 100644
index 0000000..33fe825
--- /dev/null
+++ b/src/filesys/file.h
@@ -0,0 +1,37 @@
+#ifndef FILESYS_FILE_H
+#define FILESYS_FILE_H
+
+#include "filesys/off_t.h"
+#include "filesys/directory.h"
+
+struct inode;
+
+/* Opening and closing files. */
+struct file *file_open (struct inode *);
+struct file *file_reopen (struct file *);
+void file_close (struct file *);
+struct inode *file_get_inode (struct file *);
+
+/* Reading and writing. */
+off_t file_read (struct file *, void *, off_t);
+off_t file_read_at (struct file *, void *, off_t size, off_t start);
+off_t file_write (struct file *, const void *, off_t);
+off_t file_write_at (struct file *, const void *, off_t size, off_t start);
+
+/* Preventing writes. */
+void file_deny_write (struct file *);
+void file_allow_write (struct file *);
+
+/* File position. */
+void file_seek (struct file *, off_t);
+off_t file_tell (struct file *);
+off_t file_length (struct file *);
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+void set_file_dir(struct file *file, struct dir* dir);
+struct dir* get_file_dir(struct file* file);
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+#endif /* filesys/file.h */
diff --git a/src/filesys/filesys.c b/src/filesys/filesys.c
new file mode 100644
index 0000000..c2fa356
--- /dev/null
+++ b/src/filesys/filesys.c
@@ -0,0 +1,331 @@
+#include "filesys/filesys.h"
+#include <debug.h>
+#include <stdio.h>
+#include <string.h>
+#include "filesys/file.h"
+#include "filesys/free-map.h"
+#include "filesys/inode.h"
+#include "filesys/directory.h"
+#include "filesys/cache.h"
+#include "threads/malloc.h"
+#include "lib/user/syscall.h"
+#include "threads/thread.h"
+
+
+/* Partition that contains the file system. */
+struct block *fs_device;
+
+static void do_format (void);
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+/*
+ * Check whether a single file/dir name is valid
+ * Only check the length and '/'
+ * */
+bool check_filedir_name(const char *name)
+{
+  if (name == NULL)
+    return false;
+  for (int i = 0; i < READDIR_MAX_LEN + 1; i++)
+  {
+    if (name[i] == '/')
+      return false;
+    if (name[i] == '\0')
+      return true;
+  }
+  return false;
+}
+
+/*
+ * Check whether the name is '/'.
+ * */
+bool is_rootpath(const char *name)
+{
+  if (name == NULL)
+    return false;
+  if (name[0] == '/' && name[1] == '\0')
+    return true;
+  return false;
+}
+
+/*
+ * Paser non root path to a form as:
+ * previous directory + target file/dir name
+ * is_dir is true if it's surely a dir, false if unknown.
+ * if return true, prev_dir MUST be closed after use this function
+ * */
+bool
+path_paser(const char *target_path, struct dir **prev_dir, char **pure_name, bool *is_dir)
+{
+  *is_dir = false; // init it to false;
+//  printf("%s\n", target_path);
+  // copy the full path
+  int length = strlen(target_path);
+  if (length == 0)
+    return false;
+  char *path_copy = malloc(length + 1);
+  strlcpy(path_copy, target_path, length + 1);
+
+  // check the tail to check whether it is surely a dir.
+  if(length > 0 && path_copy[length - 1] == '/')
+  {
+    *is_dir = true;
+    length--;
+    ASSERT(length > 0); // non-root dir, length shouldn't be 0 here.
+    path_copy[length] = '\0';
+  }
+
+  // length 0 is invalid.
+  if (length == 0)
+  {
+    free(path_copy);
+    return false;
+  }
+
+  if (path_copy[0] == '/')
+    *prev_dir = dir_open_root();
+  else
+    *prev_dir = dir_reopen(thread_current()->current_dir);
+
+  //split token by '/'
+  char *token, *save_ptr, *next_token;
+  for (token = strtok_r(path_copy, "/", &save_ptr); ;token = next_token)
+  {
+    if (!check_filedir_name(token))
+    {
+      free(path_copy);
+      dir_close(*prev_dir);
+      return false;
+    }
+    ASSERT(token != NULL);
+    next_token = strtok_r(NULL, "/", &save_ptr);
+    if (next_token == NULL) // token is the purename in path
+    {
+      strlcpy(*pure_name, token, READDIR_MAX_LEN + 1);
+      break;
+    }
+    else
+    {
+      struct dir *tmp_dir = *prev_dir;
+      *prev_dir = subdir_lookup(*prev_dir, token);
+      dir_close(tmp_dir);
+      if (*prev_dir == NULL)
+      {
+        free(path_copy);
+        return false;
+      }
+    }
+  }
+  free(path_copy);
+  return true;
+}
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+
+/* Initializes the file system module.
+   If FORMAT is true, reformats the file system. */
+void
+filesys_init (bool format) 
+{
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+  cache_init ();
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+  fs_device = block_get_role (BLOCK_FILESYS); //Get all filesys blocks.
+  if (fs_device == NULL)
+    PANIC ("No file system device found, can't initialize file system.");
+
+  inode_init ();
+  free_map_init ();
+
+  if (format) 
+    do_format ();
+
+  free_map_open ();
+}
+
+/* Shuts down the file system module, writing any unwritten data
+   to disk. */
+void
+filesys_done (void) 
+{
+  free_map_close ();
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+  cache_done ();
+/* ----- Newly Add for Proj04 FileSys ----- */
+}
+
+/* Creates a file named NAME with the given INITIAL_SIZE.
+   Returns true if successful, false otherwise.
+   Fails if a file named NAME already exists,
+   or if internal memory allocation fails. */
+bool
+filesys_create (const char *name, off_t initial_size) 
+{
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+  struct dir *file_dir;
+  char *pure_name = malloc(READDIR_MAX_LEN + 1);
+  bool is_dir;
+  ASSERT(name != NULL);
+  ASSERT(pure_name != NULL);
+  if(strlen(name) > 0 && !is_rootpath(name) && path_paser(name, &file_dir, &pure_name, &is_dir))
+  {
+    ASSERT(file_dir != NULL);
+    ASSERT(pure_name != NULL);
+    if (is_dir)
+    {
+      dir_close(file_dir);
+      free(pure_name);
+      return false;
+    }
+    bool res = subfile_create(file_dir, pure_name, initial_size);
+    dir_close(file_dir);
+    free(pure_name);
+    return res;
+  }
+  else
+  {
+    free(pure_name);
+    return false;
+  }
+#else
+  block_sector_t inode_sector = 0;
+  struct dir *dir = dir_open_root ();
+  bool success = (dir != NULL
+                  && free_map_allocate (1, &inode_sector)
+                  && inode_create (inode_sector, initial_size)
+                  && dir_add (dir, name, inode_sector));
+  if (!success && inode_sector != 0)
+    free_map_release (inode_sector, 1);
+  dir_close (dir);
+
+  return success;
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+}
+
+/* Opens the file with the given NAME.
+   Returns the new file if successful or a null pointer
+   otherwise.
+   Fails if no file named NAME exists,
+   or if an internal memory allocation fails. */
+struct file *
+filesys_open (const char *name)
+{
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+#ifdef FILESYS
+  struct dir* file_dir;
+  char *pure_name = malloc(READDIR_MAX_LEN + 1);
+  bool is_dir;
+  ASSERT(name != NULL);
+  ASSERT(pure_name != NULL);
+  if (strlen(name) > 0 && is_rootpath(name))
+  {
+    struct file* res = file_open(inode_open(ROOT_DIR_SECTOR));
+    set_file_dir(res, dir_open_root());
+    return res;
+  }
+  if(strlen(name) > 0 && path_paser(name, &file_dir, &pure_name, &is_dir))
+  {
+    ASSERT(file_dir != NULL);
+    ASSERT(pure_name != NULL);
+    struct dir *res_dir;
+    struct file *res_file;
+    struct file* res;
+    res_dir = subdir_lookup(file_dir, pure_name);
+    res_file = subfile_lookup(file_dir, pure_name);
+    if (is_dir || res_dir != NULL)
+    {
+      res = file_open(inode_reopen(dir_get_inode(res_dir)));
+      set_file_dir(res, dir_reopen(file_dir));
+      dir_close(res_dir);
+    }
+    else
+      res = res_file;
+    dir_close(file_dir);
+    free(pure_name);
+    return res;
+  }
+  else
+  {
+    free(pure_name);
+    return NULL;
+  }
+#else
+  struct dir *dir = dir_open_root ();
+  struct inode *inode = NULL;
+
+  if (dir != NULL)
+    dir_lookup (dir, name, &inode);
+  dir_close (dir);
+
+  return file_open (inode);
+#endif
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+}
+
+/* Deletes the file named NAME.
+   Returns true if successful, false on failure.
+   Fails if no file named NAME exists,
+   or if an internal memory allocation fails. */
+bool
+filesys_remove (const char *name) 
+{
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+  struct dir* file_dir;
+  char *pure_name = malloc(READDIR_MAX_LEN + 1);
+  bool is_dir;
+  ASSERT(name != NULL);
+  ASSERT(pure_name != NULL);
+  if(strlen(name) > 0 && !is_rootpath(name) && path_paser(name, &file_dir, &pure_name, &is_dir))
+  {
+    ASSERT(file_dir != NULL);
+    ASSERT(pure_name != NULL);
+    bool res_dir = false, res_file = false;
+    if (is_dir)
+      res_dir = subdir_delete(file_dir, pure_name);
+    else
+    {
+      res_dir = subdir_delete(file_dir, pure_name);
+      res_file = subfile_delete(file_dir, pure_name);
+    }
+    dir_close(file_dir);
+    free(pure_name);
+    return res_dir || res_file;
+  }
+  else
+  {
+    free(pure_name);
+    return false;
+  }
+#else
+  struct dir *dir = dir_open_root ();
+  bool success = dir != NULL && dir_remove (dir, name);
+  dir_close (dir);
+
+  return success;
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+}
+
+/* Formats the file system. */
+static void
+do_format (void)
+{
+  printf ("Formatting file system...");
+  free_map_create ();
+  if (!dir_create (ROOT_DIR_SECTOR, 16))
+    PANIC ("root directory creation failed");
+  free_map_close ();
+  printf ("done.\n");
+}
diff --git a/src/filesys/filesys.h b/src/filesys/filesys.h
new file mode 100644
index 0000000..09fdea5
--- /dev/null
+++ b/src/filesys/filesys.h
@@ -0,0 +1,27 @@
+#ifndef FILESYS_FILESYS_H
+#define FILESYS_FILESYS_H
+
+#include <stdbool.h>
+#include "filesys/off_t.h"
+#include "filesys/directory.h"
+
+/* Sectors of system file inodes. */
+#define FREE_MAP_SECTOR 0       /* Free map file inode sector. */
+#define ROOT_DIR_SECTOR 1       /* Root directory file inode sector. */
+
+/* Block device that contains the file system. */
+extern struct block *fs_device;
+
+void filesys_init (bool format);
+void filesys_done (void);
+bool filesys_create (const char *name, off_t initial_size);
+struct file *filesys_open (const char *name);
+bool filesys_remove (const char *name);
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+bool check_filedir_name(const char *name);
+bool is_rootpath(const char *name);
+bool path_paser(const char *target_path, struct dir **prev_dir, char **pure_name, bool *is_dir);
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+#endif /* filesys/filesys.h */
diff --git a/src/filesys/free-map.c b/src/filesys/free-map.c
new file mode 100644
index 0000000..29ea4df
--- /dev/null
+++ b/src/filesys/free-map.c
@@ -0,0 +1,85 @@
+#include "filesys/free-map.h"
+#include <bitmap.h>
+#include <debug.h>
+#include "filesys/file.h"
+#include "filesys/filesys.h"
+#include "filesys/inode.h"
+
+static struct file *free_map_file;   /* Free map file. */
+static struct bitmap *free_map;      /* Free map, one bit per sector. */
+
+/* Initializes the free map. */
+void
+free_map_init (void) 
+{
+  free_map = bitmap_create (block_size (fs_device));
+  if (free_map == NULL)
+    PANIC ("bitmap creation failed--file system device is too large");
+  bitmap_mark (free_map, FREE_MAP_SECTOR);
+  bitmap_mark (free_map, ROOT_DIR_SECTOR);
+}
+
+/* Allocates CNT consecutive sectors from the free map and stores
+   the first into *SECTORP.
+   Returns true if successful, false if not enough consecutive
+   sectors were available or if the free_map file could not be
+   written. */
+bool
+free_map_allocate (size_t cnt, block_sector_t *sectorp)
+{
+  block_sector_t sector = bitmap_scan_and_flip (free_map, 0, cnt, false);
+  if (sector != BITMAP_ERROR
+      && free_map_file != NULL
+      && !bitmap_write (free_map, free_map_file))
+    {
+      bitmap_set_multiple (free_map, sector, cnt, false); 
+      sector = BITMAP_ERROR;
+    }
+  if (sector != BITMAP_ERROR)
+    *sectorp = sector;
+  return sector != BITMAP_ERROR;
+}
+
+/* Makes CNT sectors starting at SECTOR available for use. */
+void
+free_map_release (block_sector_t sector, size_t cnt)
+{
+  ASSERT (bitmap_all (free_map, sector, cnt));
+  bitmap_set_multiple (free_map, sector, cnt, false);
+  bitmap_write (free_map, free_map_file);
+}
+
+/* Opens the free map file and reads it from disk. */
+void
+free_map_open (void) 
+{
+  free_map_file = file_open (inode_open (FREE_MAP_SECTOR));
+  if (free_map_file == NULL)
+    PANIC ("can't open free map");
+  if (!bitmap_read (free_map, free_map_file))
+    PANIC ("can't read free map");
+}
+
+/* Writes the free map to disk and closes the free map file. */
+void
+free_map_close (void) 
+{
+  file_close (free_map_file);
+}
+
+/* Creates a new free map file on disk and writes the free map to
+   it. */
+void
+free_map_create (void) 
+{
+  /* Create inode. */
+  if (!inode_create (FREE_MAP_SECTOR, bitmap_file_size (free_map)))
+    PANIC ("free map creation failed");
+
+  /* Write bitmap to file. */
+  free_map_file = file_open (inode_open (FREE_MAP_SECTOR));
+  if (free_map_file == NULL)
+    PANIC ("can't open free map");
+  if (!bitmap_write (free_map, free_map_file))
+    PANIC ("can't write free map");
+}
diff --git a/src/filesys/free-map.h b/src/filesys/free-map.h
new file mode 100644
index 0000000..316cd1c
--- /dev/null
+++ b/src/filesys/free-map.h
@@ -0,0 +1,17 @@
+#ifndef FILESYS_FREE_MAP_H
+#define FILESYS_FREE_MAP_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include "devices/block.h"
+
+void free_map_init (void);
+void free_map_read (void);
+void free_map_create (void);
+void free_map_open (void);
+void free_map_close (void);
+
+bool free_map_allocate (size_t, block_sector_t *);
+void free_map_release (block_sector_t, size_t);
+
+#endif /* filesys/free-map.h */
diff --git a/src/filesys/fsutil.c b/src/filesys/fsutil.c
new file mode 100644
index 0000000..df632df
--- /dev/null
+++ b/src/filesys/fsutil.c
@@ -0,0 +1,223 @@
+#include "filesys/fsutil.h"
+#include <debug.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ustar.h>
+#include "filesys/directory.h"
+#include "filesys/file.h"
+#include "filesys/filesys.h"
+#include "threads/malloc.h"
+#include "threads/palloc.h"
+#include "threads/vaddr.h"
+
+/* List files in the root directory. */
+void
+fsutil_ls (char **argv UNUSED) 
+{
+  struct dir *dir;
+  char name[NAME_MAX + 1];
+  
+  printf ("Files in the root directory:\n");
+  dir = dir_open_root ();
+  if (dir == NULL)
+    PANIC ("root dir open failed");
+  while (dir_readdir (dir, name))
+    printf ("%s\n", name);
+  dir_close (dir);
+  printf ("End of listing.\n");
+}
+
+/* Prints the contents of file ARGV[1] to the system console as
+   hex and ASCII. */
+void
+fsutil_cat (char **argv)
+{
+  const char *file_name = argv[1];
+  
+  struct file *file;
+  char *buffer;
+
+  printf ("Printing '%s' to the console...\n", file_name);
+  file = filesys_open (file_name);
+  if (file == NULL)
+    PANIC ("%s: open failed", file_name);
+  buffer = palloc_get_page (PAL_ASSERT);
+  for (;;) 
+    {
+      off_t pos = file_tell (file);
+      off_t n = file_read (file, buffer, PGSIZE);
+      if (n == 0)
+        break;
+
+      hex_dump (pos, buffer, n, true); 
+    }
+  palloc_free_page (buffer);
+  file_close (file);
+}
+
+/* Deletes file ARGV[1]. */
+void
+fsutil_rm (char **argv) 
+{
+  const char *file_name = argv[1];
+  
+  printf ("Deleting '%s'...\n", file_name);
+  if (!filesys_remove (file_name))
+    PANIC ("%s: delete failed\n", file_name);
+}
+
+/* Extracts a ustar-format tar archive from the scratch block
+   device into the Pintos file system. */
+void
+fsutil_extract (char **argv UNUSED) 
+{
+  static block_sector_t sector = 0;
+
+  struct block *src;
+  void *header, *data;
+
+  /* Allocate buffers. */
+  header = malloc (BLOCK_SECTOR_SIZE);
+  data = malloc (BLOCK_SECTOR_SIZE);
+  if (header == NULL || data == NULL)
+    PANIC ("couldn't allocate buffers");
+
+  /* Open source block device. */
+  src = block_get_role (BLOCK_SCRATCH);
+  if (src == NULL)
+    PANIC ("couldn't open scratch device");
+
+  printf ("Extracting ustar archive from scratch device "
+          "into file system...\n");
+
+  for (;;)
+    {
+      const char *file_name;
+      const char *error;
+      enum ustar_type type;
+      int size;
+
+      /* Read and parse ustar header. */
+      block_read (src, sector++, header);
+      error = ustar_parse_header (header, &file_name, &type, &size);
+      if (error != NULL)
+        PANIC ("bad ustar header in sector %"PRDSNu" (%s)", sector - 1, error);
+
+      if (type == USTAR_EOF)
+        {
+          /* End of archive. */
+          break;
+        }
+      else if (type == USTAR_DIRECTORY)
+        printf ("ignoring directory %s\n", file_name);
+      else if (type == USTAR_REGULAR)
+        {
+          struct file *dst;
+
+          printf ("Putting '%s' into the file system...\n", file_name);
+
+          /* Create destination file. */
+          if (!filesys_create (file_name, size))
+            PANIC ("%s: create failed", file_name);
+          dst = filesys_open (file_name);
+          if (dst == NULL)
+            PANIC ("%s: open failed", file_name);
+
+          /* Do copy. */
+          while (size > 0)
+            {
+              int chunk_size = (size > BLOCK_SECTOR_SIZE
+                                ? BLOCK_SECTOR_SIZE
+                                : size);
+              block_read (src, sector++, data);
+              if (file_write (dst, data, chunk_size) != chunk_size)
+                PANIC ("%s: write failed with %d bytes unwritten",
+                       file_name, size);
+              size -= chunk_size;
+            }
+
+          /* Finish up. */
+          file_close (dst);
+        }
+    }
+
+  /* Erase the ustar header from the start of the block device,
+     so that the extraction operation is idempotent.  We erase
+     two blocks because two blocks of zeros are the ustar
+     end-of-archive marker. */
+  printf ("Erasing ustar archive...\n");
+  memset (header, 0, BLOCK_SECTOR_SIZE);
+  block_write (src, 0, header);
+  block_write (src, 1, header);
+
+  free (data);
+  free (header);
+}
+
+/* Copies file FILE_NAME from the file system to the scratch
+   device, in ustar format.
+
+   The first call to this function will write starting at the
+   beginning of the scratch device.  Later calls advance across
+   the device.  This position is independent of that used for
+   fsutil_extract(), so `extract' should precede all
+   `append's. */
+void
+fsutil_append (char **argv)
+{
+  static block_sector_t sector = 0;
+
+  const char *file_name = argv[1];
+  void *buffer;
+  struct file *src;
+  struct block *dst;
+  off_t size;
+
+  printf ("Appending '%s' to ustar archive on scratch device...\n", file_name);
+
+  /* Allocate buffer. */
+  buffer = malloc (BLOCK_SECTOR_SIZE);
+  if (buffer == NULL)
+    PANIC ("couldn't allocate buffer");
+
+  /* Open source file. */
+  src = filesys_open (file_name);
+  if (src == NULL)
+    PANIC ("%s: open failed", file_name);
+  size = file_length (src);
+
+  /* Open target block device. */
+  dst = block_get_role (BLOCK_SCRATCH);
+  if (dst == NULL)
+    PANIC ("couldn't open scratch device");
+  
+  /* Write ustar header to first sector. */
+  if (!ustar_make_header (file_name, USTAR_REGULAR, size, buffer))
+    PANIC ("%s: name too long for ustar format", file_name);
+  block_write (dst, sector++, buffer);
+
+  /* Do copy. */
+  while (size > 0) 
+    {
+      int chunk_size = size > BLOCK_SECTOR_SIZE ? BLOCK_SECTOR_SIZE : size;
+      if (sector >= block_size (dst))
+        PANIC ("%s: out of space on scratch device", file_name);
+      if (file_read (src, buffer, chunk_size) != chunk_size)
+        PANIC ("%s: read failed with %"PROTd" bytes unread", file_name, size);
+      memset (buffer + chunk_size, 0, BLOCK_SECTOR_SIZE - chunk_size);
+      block_write (dst, sector++, buffer);
+      size -= chunk_size;
+    }
+
+  /* Write ustar end-of-archive marker, which is two consecutive
+     sectors full of zeros.  Don't advance our position past
+     them, though, in case we have more files to append. */
+  memset (buffer, 0, BLOCK_SECTOR_SIZE);
+  block_write (dst, sector, buffer);
+  block_write (dst, sector + 1, buffer);
+
+  /* Finish up. */
+  file_close (src);
+  free (buffer);
+}
diff --git a/src/filesys/fsutil.h b/src/filesys/fsutil.h
new file mode 100644
index 0000000..cc73705
--- /dev/null
+++ b/src/filesys/fsutil.h
@@ -0,0 +1,10 @@
+#ifndef FILESYS_FSUTIL_H
+#define FILESYS_FSUTIL_H
+
+void fsutil_ls (char **argv);
+void fsutil_cat (char **argv);
+void fsutil_rm (char **argv);
+void fsutil_extract (char **argv);
+void fsutil_append (char **argv);
+
+#endif /* filesys/fsutil.h */
diff --git a/src/filesys/inode.c b/src/filesys/inode.c
new file mode 100644
index 0000000..f3857d2
--- /dev/null
+++ b/src/filesys/inode.c
@@ -0,0 +1,515 @@
+#include "filesys/inode.h"
+#include <list.h>
+#include <debug.h>
+#include <round.h>
+#include <string.h>
+#include "filesys/filesys.h"
+#include "filesys/free-map.h"
+#include "filesys/cache.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+
+/* Identifies an inode. */
+#define INODE_MAGIC 0x494e4f44
+#define TABLE_SIZE 128
+
+static char zeros[BLOCK_SECTOR_SIZE];
+static char empty[BLOCK_SECTOR_SIZE];
+
+/* On-disk inode.
+   Must be exactly BLOCK_SECTOR_SIZE bytes long. */
+struct inode_disk
+  {
+    block_sector_t table;               /* Table data sector. */
+    off_t length;                       /* File size in bytes. */
+    unsigned magic;                     /* Magic number. */
+    bool is_dir;                        /* Inode for a directory. */
+    uint8_t unused[499];                /* Not used. */
+  };
+
+/* Returns the number of sectors to allocate for an inode SIZE
+   bytes long. */
+static inline size_t
+bytes_to_sectors (off_t size)
+{
+  return DIV_ROUND_UP (size, BLOCK_SECTOR_SIZE);
+}
+
+/* In-memory inode. */
+struct inode 
+  {
+    struct list_elem elem;              /* Element in inode list. */
+    block_sector_t sector;              /* Sector number of disk location. */
+    int open_cnt;                       /* Number of openers. */
+    bool removed;                       /* True if deleted, false otherwise. */
+    int deny_write_cnt;                 /* 0: writes ok, >0: deny writes. */
+    struct inode_disk data;             /* Inode content. */
+  };
+
+/* Map the pos into tables
+*/
+
+static off_t
+byte_to_t1(off_t pos)
+{
+  return (pos >> 16) & (TABLE_SIZE - 1);
+}
+
+static off_t
+byte_to_t2(off_t pos)
+{
+  return (pos >> 9) & (TABLE_SIZE - 1);
+}
+
+/* Returns the block device sector that contains byte offset POS
+   within INODE.
+   Returns -1 if INODE does not contain data for a byte at offset
+   POS. */
+
+static block_sector_t
+byte_to_sector (struct inode *inode, off_t pos, bool create) 
+{
+  ASSERT (inode != NULL);
+  
+  block_sector_t *t1 = calloc(TABLE_SIZE, sizeof *t1);
+  block_sector_t *t2 = calloc(TABLE_SIZE, sizeof *t2);
+
+  if (!(pos < inode->data.length))
+    {
+      if (!create)
+        {
+          free(t1);
+          free(t2);
+          return -1;
+        }
+      else
+        {
+          off_t i, j;
+          off_t t1_s = byte_to_t1(inode->data.length);
+          off_t t2_s = byte_to_t2(inode->data.length);
+          off_t t1_t = byte_to_t1(pos);
+          off_t t2_t = byte_to_t2(pos);
+          
+          cache_read (inode->data.table, t1);
+          for (i = t1_s; i <= t1_t; i++)
+            {
+              off_t l = (i == t1_s ? t2_s : 0);
+              off_t r = (i == t1_t ? t2_t : TABLE_SIZE - 1);
+              
+              if (t1[i] == -1)
+                {
+                  if (!free_map_allocate (1, &t1[i]))
+                  {
+                    free(t1);
+                    free(t2);
+                    return -1;
+                  }
+                  cache_write (t1[i], empty);
+                }
+                
+              cache_read (t1[i], t2);
+              for(j = l; j <= r; j++)
+                {
+                  if (t2[j] == -1)
+                    {
+                      if (!free_map_allocate (1, &t2[j])) {
+                        free(t1);
+                        free(t2);
+                        return -1;
+                      }
+                      cache_write (t2[j], zeros);
+                    }
+                }
+              cache_write (t1[i], t2);
+            }
+          cache_write (inode->data.table, t1);
+          
+          inode->data.length = pos + 1;
+          cache_write (inode->sector, &inode->data);
+        }
+    }
+  
+  cache_read (inode->data.table, t1);
+  cache_read (t1[byte_to_t1 (pos)], t2);
+  block_sector_t result = t2[byte_to_t2 (pos)];
+  
+  free(t1);
+  free(t2);
+  return result;
+}
+
+/* List of open inodes, so that opening a single inode twice
+   returns the same `struct inode'. */
+static struct list open_inodes;
+
+/* Initializes the inode module. */
+void
+inode_init (void) 
+{
+  list_init (&open_inodes);
+  memset(empty, -1, sizeof empty);
+}
+
+/* Initializes an inode with LENGTH bytes of data and
+   writes the new inode to sector SECTOR on the file system
+   device.
+   Returns true if successful.
+   Returns false if memory or disk allocation fails. */
+bool
+inode_create (block_sector_t sector, off_t length)
+{
+  struct inode_disk *disk_inode = NULL;
+  bool success = false;
+
+  ASSERT (length >= 0);
+
+  /* If this assertion fails, the inode structure is not exactly
+     one sector in size, and you should fix that. */
+  ASSERT (sizeof *disk_inode == BLOCK_SECTOR_SIZE);
+
+  disk_inode = calloc (1, sizeof *disk_inode);
+  if (disk_inode != NULL)
+    {
+      disk_inode->length = length;
+      disk_inode->magic = INODE_MAGIC;
+      disk_inode->is_dir = false;
+      if (free_map_allocate (1, &disk_inode->table)) 
+        {
+          cache_write (sector, disk_inode);
+          cache_write (disk_inode->table, empty);
+          
+          if(length > 0)
+            {
+              block_sector_t *t1 = calloc(TABLE_SIZE, sizeof *t1);
+              block_sector_t *t2 = calloc(TABLE_SIZE, sizeof *t2);
+              
+              int i, j;
+              off_t t1_t = byte_to_t1(length - 1);
+              off_t t2_t = byte_to_t2(length - 1);
+          
+              cache_read (disk_inode->table, t1);
+              for(i = 0; i <= t1_t; i++)
+                {
+                  off_t r = (i == t1_t ? t2_t : TABLE_SIZE - 1);
+              
+                  if (!free_map_allocate (1, &t1[i]))
+                  {
+                    free(t1);
+                    free(t2);
+                    free (disk_inode);
+                    return false;
+                  }
+                  cache_write(t1[i], empty);
+                 
+                  cache_read (t1[i], t2);
+                  for(j = 0; j <= r; j++)
+                    {
+                      if (!free_map_allocate (1, &t2[j]))
+                      {
+                        free(t1);
+                        free(t2);
+                        free (disk_inode);
+                        return false;
+                      }
+                      cache_write(t2[j], zeros);
+                    }
+                  cache_write (t1[i], t2);
+                }
+              cache_write (disk_inode->table, t1);
+             
+              free(t1);
+              free(t2);
+            }      
+          success = true; 
+        } 
+      free (disk_inode);
+    }
+  return success;
+}
+
+/* Reads an inode from SECTOR
+   and returns a `struct inode' that contains it.
+   Returns a null pointer if memory allocation fails. */
+struct inode *
+inode_open (block_sector_t sector)
+{
+  struct list_elem *e;
+  struct inode *inode;
+
+  /* Check whether this inode is already open. */
+  for (e = list_begin (&open_inodes); e != list_end (&open_inodes);
+       e = list_next (e)) 
+    {
+      inode = list_entry (e, struct inode, elem);
+      if (inode->sector == sector) 
+        {
+          inode_reopen (inode);
+          return inode; 
+        }
+    }
+
+  /* Allocate memory. */
+  inode = malloc (sizeof *inode);
+  if (inode == NULL)
+    return NULL;
+
+  /* Initialize. */
+  list_push_front (&open_inodes, &inode->elem);
+  inode->sector = sector;
+  inode->open_cnt = 1;
+  inode->deny_write_cnt = 0;
+  inode->removed = false;
+  cache_read (inode->sector, &inode->data);
+  return inode;
+}
+
+/* Reopens and returns INODE. */
+struct inode *
+inode_reopen (struct inode *inode)
+{
+  if (inode != NULL)
+    inode->open_cnt++;
+  return inode;
+}
+
+/* Returns INODE's inode number. */
+block_sector_t
+inode_get_inumber (const struct inode *inode)
+{
+  return inode->sector;
+}
+
+/* Closes INODE and writes it to disk.
+   If this was the last reference to INODE, frees its memory.
+   If INODE was also a removed inode, frees its blocks. */
+void
+inode_close (struct inode *inode) 
+{
+  /* Ignore null pointer. */
+  if (inode == NULL)
+    return;
+
+  /* Release resources if this was the last opener. */
+  if (--inode->open_cnt == 0)
+    {
+      /* Remove from inode list and release lock. */
+      list_remove (&inode->elem);
+ 
+      /* Deallocate blocks if removed. */
+      if (inode->removed) 
+        {
+          off_t length = inode->data.length;
+
+          if(length > 0)
+            {
+              block_sector_t *t1 = calloc(TABLE_SIZE, sizeof *t1);
+              block_sector_t *t2 = calloc(TABLE_SIZE, sizeof *t2);
+              
+              int i, j;
+              off_t t1_t = byte_to_t1(length - 1);
+              off_t t2_t = byte_to_t2(length - 1);
+          
+              cache_read (inode->data.table, t1);
+              for(i = 0; i <= t1_t; i++)
+                {
+                  off_t r = (i == t1_t ? t2_t : TABLE_SIZE - 1);
+              
+                  cache_read (t1[i], t2);
+                  for(j = 0; j <= r; j++)
+                    {
+                      free_map_release(t2[j], 1);
+                    }
+                  free_map_release(t1[i], 1);
+                }
+             
+              free(t1);
+              free(t2);
+            }
+
+          free_map_release (inode->sector, 1);
+          free_map_release (inode->data.table, 1);
+        }
+
+      free (inode); 
+    }
+}
+
+/* Marks INODE to be deleted when it is closed by the last caller who
+   has it open. */
+void
+inode_remove (struct inode *inode) 
+{
+  ASSERT (inode != NULL);
+  inode->removed = true;
+}
+
+/* Reads SIZE bytes from INODE into BUFFER, starting at position OFFSET.
+   Returns the number of bytes actually read, which may be less
+   than SIZE if an error occurs or end of file is reached. */
+off_t
+inode_read_at (struct inode *inode, void *buffer_, off_t size, off_t offset) 
+{
+  uint8_t *buffer = buffer_;
+  off_t bytes_read = 0;
+  uint8_t *bounce = NULL;
+
+  while (size > 0) 
+    {
+      /* Disk sector to read, starting byte offset within sector. */
+      block_sector_t sector_idx = byte_to_sector (inode, offset, false);
+      int sector_ofs = offset % BLOCK_SECTOR_SIZE;
+
+      /* Bytes left in inode, bytes left in sector, lesser of the two. */
+      off_t inode_left = inode_length (inode) - offset;
+      int sector_left = BLOCK_SECTOR_SIZE - sector_ofs;
+      int min_left = inode_left < sector_left ? inode_left : sector_left;
+
+      /* Number of bytes to actually copy out of this sector. */
+      int chunk_size = size < min_left ? size : min_left;
+      if (chunk_size <= 0)
+        break;
+
+      if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE)
+        {
+          /* Read full sector directly into caller's buffer. */
+          cache_read (sector_idx, buffer + bytes_read);
+        }
+      else 
+        {
+          /* Read sector into bounce buffer, then partially copy
+             into caller's buffer. */
+          if (bounce == NULL) 
+            {
+              bounce = malloc (BLOCK_SECTOR_SIZE);
+              if (bounce == NULL)
+                break;
+            }
+          cache_read (sector_idx, bounce);
+          memcpy (buffer + bytes_read, bounce + sector_ofs, chunk_size);
+        }
+      
+      /* Advance. */
+      size -= chunk_size;
+      offset += chunk_size;
+      bytes_read += chunk_size;
+    }
+  free (bounce);
+
+  return bytes_read;
+}
+
+/* Writes SIZE bytes from BUFFER into INODE, starting at OFFSET.
+   Returns the number of bytes actually written, which may be
+   less than SIZE if end of file is reached or an error occurs.
+   (Normally a write at end of file would extend the inode, but
+   growth is not yet implemented.) */
+off_t
+inode_write_at (struct inode *inode, const void *buffer_, off_t size,
+                off_t offset) 
+{
+  const uint8_t *buffer = buffer_;
+  off_t bytes_written = 0;
+  uint8_t *bounce = NULL;
+
+  if (inode->deny_write_cnt)
+    return 0;
+
+  while (size > 0) 
+    {
+      /* Sector to write, starting byte offset within sector. */
+      block_sector_t sector_idx = byte_to_sector (inode, offset, true);
+      int sector_ofs = offset % BLOCK_SECTOR_SIZE;
+
+      /* Bytes left in inode, bytes left in sector, lesser of the two. */
+      off_t inode_left = inode_length (inode) - offset;
+      int sector_left = BLOCK_SECTOR_SIZE - sector_ofs;
+      int min_left = inode_left < sector_left ? inode_left : sector_left;
+
+      /* Number of bytes to actually write into this sector. */
+      int chunk_size = size < min_left ? size : min_left;
+      if (chunk_size <= 0)
+        break;
+
+      if (sector_ofs == 0 && chunk_size == BLOCK_SECTOR_SIZE)
+        {
+          /* Write full sector directly to disk. */
+          cache_write (sector_idx, buffer + bytes_written);
+        }
+      else 
+        {
+          /* We need a bounce buffer. */
+          if (bounce == NULL) 
+            {
+              bounce = malloc (BLOCK_SECTOR_SIZE);
+              if (bounce == NULL)
+                break;
+            }
+
+          /* If the sector contains data before or after the chunk
+             we're writing, then we need to read in the sector
+             first.  Otherwise we start with a sector of all zeros. */
+          if (sector_ofs > 0 || chunk_size < sector_left) 
+            cache_read (sector_idx, bounce);
+          else
+            memset (bounce, 0, BLOCK_SECTOR_SIZE);
+          memcpy (bounce + sector_ofs, buffer + bytes_written, chunk_size);
+          cache_write (sector_idx, bounce);
+        }
+
+      /* Advance. */
+      size -= chunk_size;
+      offset += chunk_size;
+      bytes_written += chunk_size;
+    }
+  free (bounce);
+
+  return bytes_written;
+}
+
+/* Disables writes to INODE.
+   May be called at most once per inode opener. */
+void
+inode_deny_write (struct inode *inode) 
+{
+  inode->deny_write_cnt++;
+  ASSERT (inode->deny_write_cnt <= inode->open_cnt);
+}
+
+/* Re-enables writes to INODE.
+   Must be called once by each inode opener who has called
+   inode_deny_write() on the inode, before closing the inode. */
+void
+inode_allow_write (struct inode *inode) 
+{
+  ASSERT (inode->deny_write_cnt > 0);
+  ASSERT (inode->deny_write_cnt <= inode->open_cnt);
+  inode->deny_write_cnt--;
+}
+
+/* Returns the length, in bytes, of INODE's data. */
+off_t
+inode_length (const struct inode *inode)
+{
+  return inode->data.length;
+}
+
+/* Returns if this inode is directory */
+bool 
+inode_isdir (const struct inode *inode)
+{
+  return inode->data.is_dir;
+}
+
+/* Set inode to be a directory. God bless it runs OK */
+void
+inode_set_dir (struct inode *inode)
+{
+  inode->data.is_dir = true;
+  cache_write (inode->sector, &inode->data);
+}
+
+int inode_get_opencnt(struct inode *inode)
+{
+  return inode->open_cnt;
+}
+
diff --git a/src/filesys/inode.h b/src/filesys/inode.h
new file mode 100644
index 0000000..0e9ca89
--- /dev/null
+++ b/src/filesys/inode.h
@@ -0,0 +1,28 @@
+#ifndef FILESYS_INODE_H
+#define FILESYS_INODE_H
+
+#include <stdbool.h>
+#include "filesys/off_t.h"
+#include "devices/block.h"
+
+struct bitmap;
+
+void inode_init (void);
+bool inode_create (block_sector_t, off_t);
+struct inode *inode_open (block_sector_t);
+struct inode *inode_reopen (struct inode *);
+block_sector_t inode_get_inumber (const struct inode *);
+void inode_close (struct inode *);
+void inode_remove (struct inode *);
+off_t inode_read_at (struct inode *, void *, off_t size, off_t offset);
+off_t inode_write_at (struct inode *, const void *, off_t size, off_t offset);
+void inode_deny_write (struct inode *);
+void inode_allow_write (struct inode *);
+off_t inode_length (const struct inode *);
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+bool inode_isdir (const struct inode *);
+void inode_set_dir (struct inode *);
+int inode_get_opencnt(struct inode *);
+/* ----- Newly Add for Proj04 FileSys ----- */
+#endif /* filesys/inode.h */
diff --git a/src/filesys/off_t.h b/src/filesys/off_t.h
new file mode 100644
index 0000000..9caff4d
--- /dev/null
+++ b/src/filesys/off_t.h
@@ -0,0 +1,15 @@
+#ifndef FILESYS_OFF_T_H
+#define FILESYS_OFF_T_H
+
+#include <stdint.h>
+
+/* An offset within a file.
+   This is a separate header because multiple headers want this
+   definition but not any others. */
+typedef int32_t off_t;
+
+/* Format specifier for printf(), e.g.:
+   printf ("offset=%"PROTd"\n", offset); */
+#define PROTd PRId32
+
+#endif /* filesys/off_t.h */
diff --git a/src/lib/arithmetic.c b/src/lib/arithmetic.c
new file mode 100644
index 0000000..bfc9b5a
--- /dev/null
+++ b/src/lib/arithmetic.c
@@ -0,0 +1,189 @@
+#include <stdint.h>
+
+/* On x86, division of one 64-bit integer by another cannot be
+   done with a single instruction or a short sequence.  Thus, GCC
+   implements 64-bit division and remainder operations through
+   function calls.  These functions are normally obtained from
+   libgcc, which is automatically included by GCC in any link
+   that it does.
+
+   Some x86-64 machines, however, have a compiler and utilities
+   that can generate 32-bit x86 code without having any of the
+   necessary libraries, including libgcc.  Thus, we can make
+   Pintos work on these machines by simply implementing our own
+   64-bit division routines, which are the only routines from
+   libgcc that Pintos requires.
+
+   Completeness is another reason to include these routines.  If
+   Pintos is completely self-contained, then that makes it that
+   much less mysterious. */
+
+/* Uses x86 DIVL instruction to divide 64-bit N by 32-bit D to
+   yield a 32-bit quotient.  Returns the quotient.
+   Traps with a divide error (#DE) if the quotient does not fit
+   in 32 bits. */
+static inline uint32_t
+divl (uint64_t n, uint32_t d)
+{
+  uint32_t n1 = n >> 32;
+  uint32_t n0 = n;
+  uint32_t q, r;
+
+  asm ("divl %4"
+       : "=d" (r), "=a" (q)
+       : "0" (n1), "1" (n0), "rm" (d));
+
+  return q;
+}
+
+/* Returns the number of leading zero bits in X,
+   which must be nonzero. */
+static int
+nlz (uint32_t x) 
+{
+  /* This technique is portable, but there are better ways to do
+     it on particular systems.  With sufficiently new enough GCC,
+     you can use __builtin_clz() to take advantage of GCC's
+     knowledge of how to do it.  Or you can use the x86 BSR
+     instruction directly. */
+  int n = 0;
+  if (x <= 0x0000FFFF)
+    {
+      n += 16;
+      x <<= 16; 
+    }
+  if (x <= 0x00FFFFFF)
+    {
+      n += 8;
+      x <<= 8; 
+    }
+  if (x <= 0x0FFFFFFF)
+    {
+      n += 4;
+      x <<= 4;
+    }
+  if (x <= 0x3FFFFFFF)
+    {
+      n += 2;
+      x <<= 2; 
+    }
+  if (x <= 0x7FFFFFFF)
+    n++;
+  return n;
+}
+
+/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the
+   quotient. */
+static uint64_t
+udiv64 (uint64_t n, uint64_t d)
+{
+  if ((d >> 32) == 0) 
+    {
+      /* Proof of correctness:
+
+         Let n, d, b, n1, and n0 be defined as in this function.
+         Let [x] be the "floor" of x.  Let T = b[n1/d].  Assume d
+         nonzero.  Then:
+             [n/d] = [n/d] - T + T
+                   = [n/d - T] + T                         by (1) below
+                   = [(b*n1 + n0)/d - T] + T               by definition of n
+                   = [(b*n1 + n0)/d - dT/d] + T
+                   = [(b(n1 - d[n1/d]) + n0)/d] + T
+                   = [(b[n1 % d] + n0)/d] + T,             by definition of %
+         which is the expression calculated below.
+
+         (1) Note that for any real x, integer i: [x] + i = [x + i].
+
+         To prevent divl() from trapping, [(b[n1 % d] + n0)/d] must
+         be less than b.  Assume that [n1 % d] and n0 take their
+         respective maximum values of d - 1 and b - 1:
+                 [(b(d - 1) + (b - 1))/d] < b
+             <=> [(bd - 1)/d] < b
+             <=> [b - 1/d] < b
+         which is a tautology.
+
+         Therefore, this code is correct and will not trap. */
+      uint64_t b = 1ULL << 32;
+      uint32_t n1 = n >> 32;
+      uint32_t n0 = n; 
+      uint32_t d0 = d;
+
+      return divl (b * (n1 % d0) + n0, d0) + b * (n1 / d0); 
+    }
+  else 
+    {
+      /* Based on the algorithm and proof available from
+         http://www.hackersdelight.org/revisions.pdf. */
+      if (n < d)
+        return 0;
+      else 
+        {
+          uint32_t d1 = d >> 32;
+          int s = nlz (d1);
+          uint64_t q = divl (n >> 1, (d << s) >> 32) >> (31 - s);
+          return n - (q - 1) * d < d ? q - 1 : q; 
+        }
+    }
+}
+
+/* Divides unsigned 64-bit N by unsigned 64-bit D and returns the
+   remainder. */
+static uint32_t
+umod64 (uint64_t n, uint64_t d)
+{
+  return n - d * udiv64 (n, d);
+}
+
+/* Divides signed 64-bit N by signed 64-bit D and returns the
+   quotient. */
+static int64_t
+sdiv64 (int64_t n, int64_t d)
+{
+  uint64_t n_abs = n >= 0 ? (uint64_t) n : -(uint64_t) n;
+  uint64_t d_abs = d >= 0 ? (uint64_t) d : -(uint64_t) d;
+  uint64_t q_abs = udiv64 (n_abs, d_abs);
+  return (n < 0) == (d < 0) ? (int64_t) q_abs : -(int64_t) q_abs;
+}
+
+/* Divides signed 64-bit N by signed 64-bit D and returns the
+   remainder. */
+static int32_t
+smod64 (int64_t n, int64_t d)
+{
+  return n - d * sdiv64 (n, d);
+}
+
+/* These are the routines that GCC calls. */
+
+long long __divdi3 (long long n, long long d);
+long long __moddi3 (long long n, long long d);
+unsigned long long __udivdi3 (unsigned long long n, unsigned long long d);
+unsigned long long __umoddi3 (unsigned long long n, unsigned long long d);
+
+/* Signed 64-bit division. */
+long long
+__divdi3 (long long n, long long d) 
+{
+  return sdiv64 (n, d);
+}
+
+/* Signed 64-bit remainder. */
+long long
+__moddi3 (long long n, long long d) 
+{
+  return smod64 (n, d);
+}
+
+/* Unsigned 64-bit division. */
+unsigned long long
+__udivdi3 (unsigned long long n, unsigned long long d) 
+{
+  return udiv64 (n, d);
+}
+
+/* Unsigned 64-bit remainder. */
+unsigned long long
+__umoddi3 (unsigned long long n, unsigned long long d) 
+{
+  return umod64 (n, d);
+}
diff --git a/src/lib/ctype.h b/src/lib/ctype.h
new file mode 100644
index 0000000..9096aca
--- /dev/null
+++ b/src/lib/ctype.h
@@ -0,0 +1,28 @@
+#ifndef __LIB_CTYPE_H
+#define __LIB_CTYPE_H
+
+static inline int islower (int c) { return c >= 'a' && c <= 'z'; }
+static inline int isupper (int c) { return c >= 'A' && c <= 'Z'; }
+static inline int isalpha (int c) { return islower (c) || isupper (c); }
+static inline int isdigit (int c) { return c >= '0' && c <= '9'; }
+static inline int isalnum (int c) { return isalpha (c) || isdigit (c); }
+static inline int isxdigit (int c) {
+  return isdigit (c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
+}
+static inline int isspace (int c) {
+  return (c == ' ' || c == '\f' || c == '\n'
+          || c == '\r' || c == '\t' || c == '\v');
+}
+static inline int isblank (int c) { return c == ' ' || c == '\t'; }
+static inline int isgraph (int c) { return c > 32 && c < 127; }
+static inline int isprint (int c) { return c >= 32 && c < 127; }
+static inline int iscntrl (int c) { return (c >= 0 && c < 32) || c == 127; }
+static inline int isascii (int c) { return c >= 0 && c < 128; }
+static inline int ispunct (int c) {
+  return isprint (c) && !isalnum (c) && !isspace (c);
+}
+
+static inline int tolower (int c) { return isupper (c) ? c - 'A' + 'a' : c; }
+static inline int toupper (int c) { return islower (c) ? c - 'a' + 'A' : c; }
+
+#endif /* lib/ctype.h */
diff --git a/src/lib/debug.c b/src/lib/debug.c
new file mode 100644
index 0000000..b4f8c2d
--- /dev/null
+++ b/src/lib/debug.c
@@ -0,0 +1,32 @@
+#include <debug.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Prints the call stack, that is, a list of addresses, one in
+   each of the functions we are nested within.  gdb or addr2line
+   may be applied to kernel.o to translate these into file names,
+   line numbers, and function names.  */
+void
+debug_backtrace (void) 
+{
+  static bool explained;
+  void **frame;
+  
+  printf ("Call stack: %p", __builtin_return_address (0));
+  for (frame = __builtin_frame_address (1);
+       (uintptr_t) frame >= 0x1000 && frame[0] != NULL;
+       frame = frame[0]) 
+    printf (" %p", frame[1]);
+  printf (".\n");
+
+  if (!explained) 
+    {
+      explained = true;
+      printf ("The `backtrace' program can make call stacks useful.\n"
+              "Read \"Backtraces\" in the \"Debugging Tools\" chapter\n"
+              "of the Pintos documentation for more information.\n");
+    }
+}
diff --git a/src/lib/debug.h b/src/lib/debug.h
new file mode 100644
index 0000000..888ab7b
--- /dev/null
+++ b/src/lib/debug.h
@@ -0,0 +1,39 @@
+#ifndef __LIB_DEBUG_H
+#define __LIB_DEBUG_H
+
+/* GCC lets us add "attributes" to functions, function
+   parameters, etc. to indicate their properties.
+   See the GCC manual for details. */
+#define UNUSED __attribute__ ((unused))
+#define NO_RETURN __attribute__ ((noreturn))
+#define NO_INLINE __attribute__ ((noinline))
+#define PRINTF_FORMAT(FMT, FIRST) __attribute__ ((format (printf, FMT, FIRST)))
+
+/* Halts the OS, printing the source file name, line number, and
+   function name, plus a user-specific message. */
+#define PANIC(...) debug_panic (__FILE__, __LINE__, __func__, __VA_ARGS__)
+
+void debug_panic (const char *file, int line, const char *function,
+                  const char *message, ...) PRINTF_FORMAT (4, 5) NO_RETURN;
+void debug_backtrace (void);
+void debug_backtrace_all (void);
+
+#endif
+
+
+
+/* This is outside the header guard so that debug.h may be
+   included multiple times with different settings of NDEBUG. */
+#undef ASSERT
+#undef NOT_REACHED
+
+#ifndef NDEBUG
+#define ASSERT(CONDITION)                                       \
+        if (CONDITION) { } else {                               \
+                PANIC ("assertion `%s' failed.", #CONDITION);   \
+        }
+#define NOT_REACHED() PANIC ("executed an unreachable statement");
+#else
+#define ASSERT(CONDITION) ((void) 0)
+#define NOT_REACHED() for (;;)
+#endif /* lib/debug.h */
diff --git a/src/lib/inttypes.h b/src/lib/inttypes.h
new file mode 100644
index 0000000..f703725
--- /dev/null
+++ b/src/lib/inttypes.h
@@ -0,0 +1,48 @@
+#ifndef __LIB_INTTYPES_H
+#define __LIB_INTTYPES_H
+
+#include <stdint.h>
+
+#define PRId8 "hhd"
+#define PRIi8 "hhi"
+#define PRIo8 "hho"
+#define PRIu8 "hhu"
+#define PRIx8 "hhx"
+#define PRIX8 "hhX"
+
+#define PRId16 "hd"
+#define PRIi16 "hi"
+#define PRIo16 "ho"
+#define PRIu16 "hu"
+#define PRIx16 "hx"
+#define PRIX16 "hX"
+
+#define PRId32 "d"
+#define PRIi32 "i"
+#define PRIo32 "o"
+#define PRIu32 "u"
+#define PRIx32 "x"
+#define PRIX32 "X"
+
+#define PRId64 "lld"
+#define PRIi64 "lli"
+#define PRIo64 "llo"
+#define PRIu64 "llu"
+#define PRIx64 "llx"
+#define PRIX64 "llX"
+
+#define PRIdMAX "jd"
+#define PRIiMAX "ji"
+#define PRIoMAX "jo"
+#define PRIuMAX "ju"
+#define PRIxMAX "jx"
+#define PRIXMAX "jX"
+
+#define PRIdPTR "td"
+#define PRIiPTR "ti"
+#define PRIoPTR "to"
+#define PRIuPTR "tu"
+#define PRIxPTR "tx"
+#define PRIXPTR "tX"
+
+#endif /* lib/inttypes.h */
diff --git a/src/lib/kernel/bitmap.c b/src/lib/kernel/bitmap.c
new file mode 100644
index 0000000..d14a98c
--- /dev/null
+++ b/src/lib/kernel/bitmap.c
@@ -0,0 +1,371 @@
+#include "bitmap.h"
+#include <debug.h>
+#include <limits.h>
+#include <round.h>
+#include <stdio.h>
+#include "threads/malloc.h"
+#ifdef FILESYS
+#include "filesys/file.h"
+#endif
+
+/* Element type.
+
+   This must be an unsigned integer type at least as wide as int.
+
+   Each bit represents one bit in the bitmap.
+   If bit 0 in an element represents bit K in the bitmap,
+   then bit 1 in the element represents bit K+1 in the bitmap,
+   and so on. */
+typedef unsigned long elem_type;
+
+/* Number of bits in an element. */
+#define ELEM_BITS (sizeof (elem_type) * CHAR_BIT)
+
+/* From the outside, a bitmap is an array of bits.  From the
+   inside, it's an array of elem_type (defined above) that
+   simulates an array of bits. */
+struct bitmap
+  {
+    size_t bit_cnt;     /* Number of bits. */
+    elem_type *bits;    /* Elements that represent bits. */
+  };
+
+/* Returns the index of the element that contains the bit
+   numbered BIT_IDX. */
+static inline size_t
+elem_idx (size_t bit_idx) 
+{
+  return bit_idx / ELEM_BITS;
+}
+
+/* Returns an elem_type where only the bit corresponding to
+   BIT_IDX is turned on. */
+static inline elem_type
+bit_mask (size_t bit_idx) 
+{
+  return (elem_type) 1 << (bit_idx % ELEM_BITS);
+}
+
+/* Returns the number of elements required for BIT_CNT bits. */
+static inline size_t
+elem_cnt (size_t bit_cnt)
+{
+  return DIV_ROUND_UP (bit_cnt, ELEM_BITS);
+}
+
+/* Returns the number of bytes required for BIT_CNT bits. */
+static inline size_t
+byte_cnt (size_t bit_cnt)
+{
+  return sizeof (elem_type) * elem_cnt (bit_cnt);
+}
+
+/* Returns a bit mask in which the bits actually used in the last
+   element of B's bits are set to 1 and the rest are set to 0. */
+static inline elem_type
+last_mask (const struct bitmap *b) 
+{
+  int last_bits = b->bit_cnt % ELEM_BITS;
+  return last_bits ? ((elem_type) 1 << last_bits) - 1 : (elem_type) -1;
+}
+
+/* Creation and destruction. */
+
+/* Creates and returns a pointer to a newly allocated bitmap with room for
+   BIT_CNT (or more) bits.  Returns a null pointer if memory allocation fails.
+   The caller is responsible for freeing the bitmap, with bitmap_destroy(),
+   when it is no longer needed. */
+struct bitmap *
+bitmap_create (size_t bit_cnt) 
+{
+  struct bitmap *b = malloc (sizeof *b);
+  if (b != NULL)
+    {
+      b->bit_cnt = bit_cnt;
+      b->bits = malloc (byte_cnt (bit_cnt));
+      if (b->bits != NULL || bit_cnt == 0)
+        {
+          bitmap_set_all (b, false);
+          return b;
+        }
+      free (b);
+    }
+  return NULL;
+}
+
+/* Creates and returns a bitmap with BIT_CNT bits in the
+   BLOCK_SIZE bytes of storage preallocated at BLOCK.
+   BLOCK_SIZE must be at least bitmap_needed_bytes(BIT_CNT). */
+struct bitmap *
+bitmap_create_in_buf (size_t bit_cnt, void *block, size_t block_size UNUSED)
+{
+  struct bitmap *b = block;
+  
+  ASSERT (block_size >= bitmap_buf_size (bit_cnt));
+
+  b->bit_cnt = bit_cnt;
+  b->bits = (elem_type *) (b + 1);
+  bitmap_set_all (b, false);
+  return b;
+}
+
+/* Returns the number of bytes required to accomodate a bitmap
+   with BIT_CNT bits (for use with bitmap_create_in_buf()). */
+size_t
+bitmap_buf_size (size_t bit_cnt) 
+{
+  return sizeof (struct bitmap) + byte_cnt (bit_cnt);
+}
+
+/* Destroys bitmap B, freeing its storage.
+   Not for use on bitmaps created by bitmap_create_in_buf(). */
+void
+bitmap_destroy (struct bitmap *b) 
+{
+  if (b != NULL) 
+    {
+      free (b->bits);
+      free (b);
+    }
+}
+
+/* Bitmap size. */
+
+/* Returns the number of bits in B. */
+size_t
+bitmap_size (const struct bitmap *b)
+{
+  return b->bit_cnt;
+}
+
+/* Setting and testing single bits. */
+
+/* Atomically sets the bit numbered IDX in B to VALUE. */
+void
+bitmap_set (struct bitmap *b, size_t idx, bool value) 
+{
+  ASSERT (b != NULL);
+  ASSERT (idx < b->bit_cnt);
+  if (value)
+    bitmap_mark (b, idx);
+  else
+    bitmap_reset (b, idx);
+}
+
+/* Atomically sets the bit numbered BIT_IDX in B to true. */
+void
+bitmap_mark (struct bitmap *b, size_t bit_idx) 
+{
+  size_t idx = elem_idx (bit_idx);
+  elem_type mask = bit_mask (bit_idx);
+
+  /* This is equivalent to `b->bits[idx] |= mask' except that it
+     is guaranteed to be atomic on a uniprocessor machine.  See
+     the description of the OR instruction in [IA32-v2b]. */
+  asm ("orl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc");
+}
+
+/* Atomically sets the bit numbered BIT_IDX in B to false. */
+void
+bitmap_reset (struct bitmap *b, size_t bit_idx) 
+{
+  size_t idx = elem_idx (bit_idx);
+  elem_type mask = bit_mask (bit_idx);
+
+  /* This is equivalent to `b->bits[idx] &= ~mask' except that it
+     is guaranteed to be atomic on a uniprocessor machine.  See
+     the description of the AND instruction in [IA32-v2a]. */
+  asm ("andl %1, %0" : "=m" (b->bits[idx]) : "r" (~mask) : "cc");
+}
+
+/* Atomically toggles the bit numbered IDX in B;
+   that is, if it is true, makes it false,
+   and if it is false, makes it true. */
+void
+bitmap_flip (struct bitmap *b, size_t bit_idx) 
+{
+  size_t idx = elem_idx (bit_idx);
+  elem_type mask = bit_mask (bit_idx);
+
+  /* This is equivalent to `b->bits[idx] ^= mask' except that it
+     is guaranteed to be atomic on a uniprocessor machine.  See
+     the description of the XOR instruction in [IA32-v2b]. */
+  asm ("xorl %1, %0" : "=m" (b->bits[idx]) : "r" (mask) : "cc");
+}
+
+/* Returns the value of the bit numbered IDX in B. */
+bool
+bitmap_test (const struct bitmap *b, size_t idx) 
+{
+  ASSERT (b != NULL);
+  ASSERT (idx < b->bit_cnt);
+  return (b->bits[elem_idx (idx)] & bit_mask (idx)) != 0;
+}
+
+/* Setting and testing multiple bits. */
+
+/* Sets all bits in B to VALUE. */
+void
+bitmap_set_all (struct bitmap *b, bool value) 
+{
+  ASSERT (b != NULL);
+
+  bitmap_set_multiple (b, 0, bitmap_size (b), value);
+}
+
+/* Sets the CNT bits starting at START in B to VALUE. */
+void
+bitmap_set_multiple (struct bitmap *b, size_t start, size_t cnt, bool value) 
+{
+  size_t i;
+  
+  ASSERT (b != NULL);
+  ASSERT (start <= b->bit_cnt);
+  ASSERT (start + cnt <= b->bit_cnt);
+
+  for (i = 0; i < cnt; i++)
+    bitmap_set (b, start + i, value);
+}
+
+/* Returns the number of bits in B between START and START + CNT,
+   exclusive, that are set to VALUE. */
+size_t
+bitmap_count (const struct bitmap *b, size_t start, size_t cnt, bool value) 
+{
+  size_t i, value_cnt;
+
+  ASSERT (b != NULL);
+  ASSERT (start <= b->bit_cnt);
+  ASSERT (start + cnt <= b->bit_cnt);
+
+  value_cnt = 0;
+  for (i = 0; i < cnt; i++)
+    if (bitmap_test (b, start + i) == value)
+      value_cnt++;
+  return value_cnt;
+}
+
+/* Returns true if any bits in B between START and START + CNT,
+   exclusive, are set to VALUE, and false otherwise. */
+bool
+bitmap_contains (const struct bitmap *b, size_t start, size_t cnt, bool value) 
+{
+  size_t i;
+  
+  ASSERT (b != NULL);
+  ASSERT (start <= b->bit_cnt);
+  ASSERT (start + cnt <= b->bit_cnt);
+
+  for (i = 0; i < cnt; i++)
+    if (bitmap_test (b, start + i) == value)
+      return true;
+  return false;
+}
+
+/* Returns true if any bits in B between START and START + CNT,
+   exclusive, are set to true, and false otherwise.*/
+bool
+bitmap_any (const struct bitmap *b, size_t start, size_t cnt) 
+{
+  return bitmap_contains (b, start, cnt, true);
+}
+
+/* Returns true if no bits in B between START and START + CNT,
+   exclusive, are set to true, and false otherwise.*/
+bool
+bitmap_none (const struct bitmap *b, size_t start, size_t cnt) 
+{
+  return !bitmap_contains (b, start, cnt, true);
+}
+
+/* Returns true if every bit in B between START and START + CNT,
+   exclusive, is set to true, and false otherwise. */
+bool
+bitmap_all (const struct bitmap *b, size_t start, size_t cnt) 
+{
+  return !bitmap_contains (b, start, cnt, false);
+}
+
+/* Finding set or unset bits. */
+
+/* Finds and returns the starting index of the first group of CNT
+   consecutive bits in B at or after START that are all set to
+   VALUE.
+   If there is no such group, returns BITMAP_ERROR. */
+size_t
+bitmap_scan (const struct bitmap *b, size_t start, size_t cnt, bool value) 
+{
+  ASSERT (b != NULL);
+  ASSERT (start <= b->bit_cnt);
+
+  if (cnt <= b->bit_cnt) 
+    {
+      size_t last = b->bit_cnt - cnt;
+      size_t i;
+      for (i = start; i <= last; i++)
+        if (!bitmap_contains (b, i, cnt, !value))
+          return i; 
+    }
+  return BITMAP_ERROR;
+}
+
+/* Finds the first group of CNT consecutive bits in B at or after
+   START that are all set to VALUE, flips them all to !VALUE,
+   and returns the index of the first bit in the group.
+   If there is no such group, returns BITMAP_ERROR.
+   If CNT is zero, returns 0.
+   Bits are set atomically, but testing bits is not atomic with
+   setting them. */
+size_t
+bitmap_scan_and_flip (struct bitmap *b, size_t start, size_t cnt, bool value)
+{
+  size_t idx = bitmap_scan (b, start, cnt, value);
+  if (idx != BITMAP_ERROR) 
+    bitmap_set_multiple (b, idx, cnt, !value);
+  return idx;
+}
+
+/* File input and output. */
+
+#ifdef FILESYS
+/* Returns the number of bytes needed to store B in a file. */
+size_t
+bitmap_file_size (const struct bitmap *b) 
+{
+  return byte_cnt (b->bit_cnt);
+}
+
+/* Reads B from FILE.  Returns true if successful, false
+   otherwise. */
+bool
+bitmap_read (struct bitmap *b, struct file *file) 
+{
+  bool success = true;
+  if (b->bit_cnt > 0) 
+    {
+      off_t size = byte_cnt (b->bit_cnt);
+      success = file_read_at (file, b->bits, size, 0) == size;
+      b->bits[elem_cnt (b->bit_cnt) - 1] &= last_mask (b);
+    }
+  return success;
+}
+
+/* Writes B to FILE.  Return true if successful, false
+   otherwise. */
+bool
+bitmap_write (const struct bitmap *b, struct file *file)
+{
+  off_t size = byte_cnt (b->bit_cnt);
+  return file_write_at (file, b->bits, size, 0) == size;
+}
+#endif /* FILESYS */
+
+/* Debugging. */
+
+/* Dumps the contents of B to the console as hexadecimal. */
+void
+bitmap_dump (const struct bitmap *b) 
+{
+  hex_dump (0, b->bits, byte_cnt (b->bit_cnt), false);
+}
+
diff --git a/src/lib/kernel/bitmap.h b/src/lib/kernel/bitmap.h
new file mode 100644
index 0000000..a50593c
--- /dev/null
+++ b/src/lib/kernel/bitmap.h
@@ -0,0 +1,51 @@
+#ifndef __LIB_KERNEL_BITMAP_H
+#define __LIB_KERNEL_BITMAP_H
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <inttypes.h>
+
+/* Bitmap abstract data type. */
+
+/* Creation and destruction. */
+struct bitmap *bitmap_create (size_t bit_cnt);
+struct bitmap *bitmap_create_in_buf (size_t bit_cnt, void *, size_t byte_cnt);
+size_t bitmap_buf_size (size_t bit_cnt);
+void bitmap_destroy (struct bitmap *);
+
+/* Bitmap size. */
+size_t bitmap_size (const struct bitmap *);
+
+/* Setting and testing single bits. */
+void bitmap_set (struct bitmap *, size_t idx, bool);
+void bitmap_mark (struct bitmap *, size_t idx);
+void bitmap_reset (struct bitmap *, size_t idx);
+void bitmap_flip (struct bitmap *, size_t idx);
+bool bitmap_test (const struct bitmap *, size_t idx);
+
+/* Setting and testing multiple bits. */
+void bitmap_set_all (struct bitmap *, bool);
+void bitmap_set_multiple (struct bitmap *, size_t start, size_t cnt, bool);
+size_t bitmap_count (const struct bitmap *, size_t start, size_t cnt, bool);
+bool bitmap_contains (const struct bitmap *, size_t start, size_t cnt, bool);
+bool bitmap_any (const struct bitmap *, size_t start, size_t cnt);
+bool bitmap_none (const struct bitmap *, size_t start, size_t cnt);
+bool bitmap_all (const struct bitmap *, size_t start, size_t cnt);
+
+/* Finding set or unset bits. */
+#define BITMAP_ERROR SIZE_MAX
+size_t bitmap_scan (const struct bitmap *, size_t start, size_t cnt, bool);
+size_t bitmap_scan_and_flip (struct bitmap *, size_t start, size_t cnt, bool);
+
+/* File input and output. */
+#ifdef FILESYS
+struct file;
+size_t bitmap_file_size (const struct bitmap *);
+bool bitmap_read (struct bitmap *, struct file *);
+bool bitmap_write (const struct bitmap *, struct file *);
+#endif
+
+/* Debugging. */
+void bitmap_dump (const struct bitmap *);
+
+#endif /* lib/kernel/bitmap.h */
diff --git a/src/lib/kernel/console.c b/src/lib/kernel/console.c
new file mode 100644
index 0000000..844b184
--- /dev/null
+++ b/src/lib/kernel/console.c
@@ -0,0 +1,191 @@
+#include <console.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include "devices/serial.h"
+#include "devices/vga.h"
+#include "threads/init.h"
+#include "threads/interrupt.h"
+#include "threads/synch.h"
+
+static void vprintf_helper (char, void *);
+static void putchar_have_lock (uint8_t c);
+
+/* The console lock.
+   Both the vga and serial layers do their own locking, so it's
+   safe to call them at any time.
+   But this lock is useful to prevent simultaneous printf() calls
+   from mixing their output, which looks confusing. */
+static struct lock console_lock;
+
+/* True in ordinary circumstances: we want to use the console
+   lock to avoid mixing output between threads, as explained
+   above.
+
+   False in early boot before the point that locks are functional
+   or the console lock has been initialized, or after a kernel
+   panics.  In the former case, taking the lock would cause an
+   assertion failure, which in turn would cause a panic, turning
+   it into the latter case.  In the latter case, if it is a buggy
+   lock_acquire() implementation that caused the panic, we'll
+   likely just recurse. */
+static bool use_console_lock;
+
+/* It's possible, if you add enough debug output to Pintos, to
+   try to recursively grab console_lock from a single thread.  As
+   a real example, I added a printf() call to palloc_free().
+   Here's a real backtrace that resulted:
+
+   lock_console()
+   vprintf()
+   printf()               - palloc() tries to grab the lock again
+   palloc_free()        
+   thread_schedule_tail() - another thread dying as we switch threads
+   schedule()
+   thread_yield()
+   intr_handler()         - timer interrupt
+   intr_set_level()
+   serial_putc()
+   putchar_have_lock()
+   putbuf()
+   sys_write()            - one process writing to the console
+   syscall_handler()
+   intr_handler()
+
+   This kind of thing is very difficult to debug, so we avoid the
+   problem by simulating a recursive lock with a depth
+   counter. */
+static int console_lock_depth;
+
+/* Number of characters written to console. */
+static int64_t write_cnt;
+
+/* Enable console locking. */
+void
+console_init (void) 
+{
+  lock_init (&console_lock);
+  use_console_lock = true;
+}
+
+/* Notifies the console that a kernel panic is underway,
+   which warns it to avoid trying to take the console lock from
+   now on. */
+void
+console_panic (void) 
+{
+  use_console_lock = false;
+}
+
+/* Prints console statistics. */
+void
+console_print_stats (void) 
+{
+  printf ("Console: %lld characters output\n", write_cnt);
+}
+
+/* Acquires the console lock. */
+static void
+acquire_console (void) 
+{
+  if (!intr_context () && use_console_lock) 
+    {
+      if (lock_held_by_current_thread (&console_lock)) 
+        console_lock_depth++; 
+      else
+        lock_acquire (&console_lock); 
+    }
+}
+
+/* Releases the console lock. */
+static void
+release_console (void) 
+{
+  if (!intr_context () && use_console_lock) 
+    {
+      if (console_lock_depth > 0)
+        console_lock_depth--;
+      else
+        lock_release (&console_lock); 
+    }
+}
+
+/* Returns true if the current thread has the console lock,
+   false otherwise. */
+static bool
+console_locked_by_current_thread (void) 
+{
+  return (intr_context ()
+          || !use_console_lock
+          || lock_held_by_current_thread (&console_lock));
+}
+
+/* The standard vprintf() function,
+   which is like printf() but uses a va_list.
+   Writes its output to both vga display and serial port. */
+int
+vprintf (const char *format, va_list args) 
+{
+  int char_cnt = 0;
+
+  acquire_console ();
+  __vprintf (format, args, vprintf_helper, &char_cnt);
+  release_console ();
+
+  return char_cnt;
+}
+
+/* Writes string S to the console, followed by a new-line
+   character. */
+int
+puts (const char *s) 
+{
+  acquire_console ();
+  while (*s != '\0')
+    putchar_have_lock (*s++);
+  putchar_have_lock ('\n');
+  release_console ();
+
+  return 0;
+}
+
+/* Writes the N characters in BUFFER to the console. */
+void
+putbuf (const char *buffer, size_t n) 
+{
+  acquire_console ();
+  while (n-- > 0)
+    putchar_have_lock (*buffer++);
+  release_console ();
+}
+
+/* Writes C to the vga display and serial port. */
+int
+putchar (int c) 
+{
+  acquire_console ();
+  putchar_have_lock (c);
+  release_console ();
+  
+  return c;
+}
+
+/* Helper function for vprintf(). */
+static void
+vprintf_helper (char c, void *char_cnt_) 
+{
+  int *char_cnt = char_cnt_;
+  (*char_cnt)++;
+  putchar_have_lock (c);
+}
+
+/* Writes C to the vga display and serial port.
+   The caller has already acquired the console lock if
+   appropriate. */
+static void
+putchar_have_lock (uint8_t c) 
+{
+  ASSERT (console_locked_by_current_thread ());
+  write_cnt++;
+  serial_putc (c);
+  vga_putc (c);
+}
diff --git a/src/lib/kernel/console.h b/src/lib/kernel/console.h
new file mode 100644
index 0000000..ab99249
--- /dev/null
+++ b/src/lib/kernel/console.h
@@ -0,0 +1,8 @@
+#ifndef __LIB_KERNEL_CONSOLE_H
+#define __LIB_KERNEL_CONSOLE_H
+
+void console_init (void);
+void console_panic (void);
+void console_print_stats (void);
+
+#endif /* lib/kernel/console.h */
diff --git a/src/lib/kernel/debug.c b/src/lib/kernel/debug.c
new file mode 100644
index 0000000..b12f4f9
--- /dev/null
+++ b/src/lib/kernel/debug.c
@@ -0,0 +1,123 @@
+#include <debug.h>
+#include <console.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include "threads/init.h"
+#include "threads/interrupt.h"
+#include "threads/thread.h"
+#include "threads/switch.h"
+#include "threads/vaddr.h"
+#include "devices/serial.h"
+#include "devices/shutdown.h"
+
+/* Halts the OS, printing the source file name, line number, and
+   function name, plus a user-specific message. */
+void
+debug_panic (const char *file, int line, const char *function,
+             const char *message, ...)
+{
+  static int level;
+  va_list args;
+
+  intr_disable ();
+  console_panic ();
+
+  level++;
+  if (level == 1) 
+    {
+      printf ("Kernel PANIC at %s:%d in %s(): ", file, line, function);
+
+      va_start (args, message);
+      vprintf (message, args);
+      printf ("\n");
+      va_end (args);
+
+      debug_backtrace ();
+    }
+  else if (level == 2)
+    printf ("Kernel PANIC recursion at %s:%d in %s().\n",
+            file, line, function);
+  else 
+    {
+      /* Don't print anything: that's probably why we recursed. */
+    }
+
+  serial_flush ();
+  shutdown ();
+  for (;;);
+}
+
+/* Print call stack of a thread.
+   The thread may be running, ready, or blocked. */
+static void
+print_stacktrace(struct thread *t, void *aux UNUSED)
+{
+  void *retaddr = NULL, **frame = NULL;
+  const char *status = "UNKNOWN";
+
+  switch (t->status) {
+    case THREAD_RUNNING:  
+      status = "RUNNING";
+      break;
+
+    case THREAD_READY:  
+      status = "READY";
+      break;
+
+    case THREAD_BLOCKED:  
+      status = "BLOCKED";
+      break;
+
+    default:
+      break;
+  }
+
+  printf ("Call stack of thread `%s' (status %s):", t->name, status);
+
+  if (t == thread_current()) 
+    {
+      frame = __builtin_frame_address (1);
+      retaddr = __builtin_return_address (0);
+    }
+  else
+    {
+      /* Retrieve the values of the base and instruction pointers
+         as they were saved when this thread called switch_threads. */
+      struct switch_threads_frame * saved_frame;
+
+      saved_frame = (struct switch_threads_frame *)t->stack;
+
+      /* Skip threads if they have been added to the all threads
+         list, but have never been scheduled.
+         We can identify because their `stack' member either points 
+         at the top of their kernel stack page, or the 
+         switch_threads_frame's 'eip' member points at switch_entry.
+         See also threads.c. */
+      if (t->stack == (uint8_t *)t + PGSIZE || saved_frame->eip == switch_entry)
+        {
+          printf (" thread was never scheduled.\n");
+          return;
+        }
+
+      frame = (void **) saved_frame->ebp;
+      retaddr = (void *) saved_frame->eip;
+    }
+
+  printf (" %p", retaddr);
+  for (; (uintptr_t) frame >= 0x1000 && frame[0] != NULL; frame = frame[0])
+    printf (" %p", frame[1]);
+  printf (".\n");
+}
+
+/* Prints call stack of all threads. */
+void
+debug_backtrace_all (void)
+{
+  enum intr_level oldlevel = intr_disable ();
+
+  thread_foreach (print_stacktrace, 0);
+  intr_set_level (oldlevel);
+}
diff --git a/src/lib/kernel/hash.c b/src/lib/kernel/hash.c
new file mode 100644
index 0000000..57eed45
--- /dev/null
+++ b/src/lib/kernel/hash.c
@@ -0,0 +1,430 @@
+/* Hash table.
+
+   This data structure is thoroughly documented in the Tour of
+   Pintos for Project 3.
+
+   See hash.h for basic information. */
+
+#include "hash.h"
+#include "../debug.h"
+#include "threads/malloc.h"
+
+#define list_elem_to_hash_elem(LIST_ELEM)                       \
+        list_entry(LIST_ELEM, struct hash_elem, list_elem)
+
+static struct list *find_bucket (struct hash *, struct hash_elem *);
+static struct hash_elem *find_elem (struct hash *, struct list *,
+                                    struct hash_elem *);
+static void insert_elem (struct hash *, struct list *, struct hash_elem *);
+static void remove_elem (struct hash *, struct hash_elem *);
+static void rehash (struct hash *);
+
+/* Initializes hash table H to compute hash values using HASH and
+   compare hash elements using LESS, given auxiliary data AUX. */
+bool
+hash_init (struct hash *h,
+           hash_hash_func *hash, hash_less_func *less, void *aux) 
+{
+  h->elem_cnt = 0;
+  h->bucket_cnt = 4;
+  h->buckets = malloc (sizeof *h->buckets * h->bucket_cnt);
+  h->hash = hash;
+  h->less = less;
+  h->aux = aux;
+
+  if (h->buckets != NULL) 
+    {
+      hash_clear (h, NULL);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Removes all the elements from H.
+   
+   If DESTRUCTOR is non-null, then it is called for each element
+   in the hash.  DESTRUCTOR may, if appropriate, deallocate the
+   memory used by the hash element.  However, modifying hash
+   table H while hash_clear() is running, using any of the
+   functions hash_clear(), hash_destroy(), hash_insert(),
+   hash_replace(), or hash_delete(), yields undefined behavior,
+   whether done in DESTRUCTOR or elsewhere. */
+void
+hash_clear (struct hash *h, hash_action_func *destructor) 
+{
+  size_t i;
+
+  for (i = 0; i < h->bucket_cnt; i++) 
+    {
+      struct list *bucket = &h->buckets[i];
+
+      if (destructor != NULL) 
+        while (!list_empty (bucket)) 
+          {
+            struct list_elem *list_elem = list_pop_front (bucket);
+            struct hash_elem *hash_elem = list_elem_to_hash_elem (list_elem);
+            destructor (hash_elem, h->aux);
+          }
+
+      list_init (bucket); 
+    }    
+
+  h->elem_cnt = 0;
+}
+
+/* Destroys hash table H.
+
+   If DESTRUCTOR is non-null, then it is first called for each
+   element in the hash.  DESTRUCTOR may, if appropriate,
+   deallocate the memory used by the hash element.  However,
+   modifying hash table H while hash_clear() is running, using
+   any of the functions hash_clear(), hash_destroy(),
+   hash_insert(), hash_replace(), or hash_delete(), yields
+   undefined behavior, whether done in DESTRUCTOR or
+   elsewhere. */
+void
+hash_destroy (struct hash *h, hash_action_func *destructor) 
+{
+  if (destructor != NULL)
+    hash_clear (h, destructor);
+  free (h->buckets);
+}
+
+/* Inserts NEW into hash table H and returns a null pointer, if
+   no equal element is already in the table.
+   If an equal element is already in the table, returns it
+   without inserting NEW. */   
+struct hash_elem *
+hash_insert (struct hash *h, struct hash_elem *new)
+{
+  struct list *bucket = find_bucket (h, new);
+  struct hash_elem *old = find_elem (h, bucket, new);
+
+  if (old == NULL) 
+    insert_elem (h, bucket, new);
+
+  rehash (h);
+
+  return old; 
+}
+
+/* Inserts NEW into hash table H, replacing any equal element
+   already in the table, which is returned. */
+struct hash_elem *
+hash_replace (struct hash *h, struct hash_elem *new) 
+{
+  struct list *bucket = find_bucket (h, new);
+  struct hash_elem *old = find_elem (h, bucket, new);
+
+  if (old != NULL)
+    remove_elem (h, old);
+  insert_elem (h, bucket, new);
+
+  rehash (h);
+
+  return old;
+}
+
+/* Finds and returns an element equal to E in hash table H, or a
+   null pointer if no equal element exists in the table. */
+struct hash_elem *
+hash_find (struct hash *h, struct hash_elem *e) 
+{
+  return find_elem (h, find_bucket (h, e), e);
+}
+
+/* Finds, removes, and returns an element equal to E in hash
+   table H.  Returns a null pointer if no equal element existed
+   in the table.
+
+   If the elements of the hash table are dynamically allocated,
+   or own resources that are, then it is the caller's
+   responsibility to deallocate them. */
+struct hash_elem *
+hash_delete (struct hash *h, struct hash_elem *e)
+{
+  struct hash_elem *found = find_elem (h, find_bucket (h, e), e);
+  if (found != NULL) 
+    {
+      remove_elem (h, found);
+      rehash (h); 
+    }
+  return found;
+}
+
+/* Calls ACTION for each element in hash table H in arbitrary
+   order. 
+   Modifying hash table H while hash_apply() is running, using
+   any of the functions hash_clear(), hash_destroy(),
+   hash_insert(), hash_replace(), or hash_delete(), yields
+   undefined behavior, whether done from ACTION or elsewhere. */
+void
+hash_apply (struct hash *h, hash_action_func *action) 
+{
+  size_t i;
+  
+  ASSERT (action != NULL);
+
+  for (i = 0; i < h->bucket_cnt; i++) 
+    {
+      struct list *bucket = &h->buckets[i];
+      struct list_elem *elem, *next;
+
+      for (elem = list_begin (bucket); elem != list_end (bucket); elem = next) 
+        {
+          next = list_next (elem);
+          action (list_elem_to_hash_elem (elem), h->aux);
+        }
+    }
+}
+
+/* Initializes I for iterating hash table H.
+
+   Iteration idiom:
+
+      struct hash_iterator i;
+
+      hash_first (&i, h);
+      while (hash_next (&i))
+        {
+          struct foo *f = hash_entry (hash_cur (&i), struct foo, elem);
+          ...do something with f...
+        }
+
+   Modifying hash table H during iteration, using any of the
+   functions hash_clear(), hash_destroy(), hash_insert(),
+   hash_replace(), or hash_delete(), invalidates all
+   iterators. */
+void
+hash_first (struct hash_iterator *i, struct hash *h) 
+{
+  ASSERT (i != NULL);
+  ASSERT (h != NULL);
+
+  i->hash = h;
+  i->bucket = i->hash->buckets;
+  i->elem = list_elem_to_hash_elem (list_head (i->bucket));
+}
+
+/* Advances I to the next element in the hash table and returns
+   it.  Returns a null pointer if no elements are left.  Elements
+   are returned in arbitrary order.
+
+   Modifying a hash table H during iteration, using any of the
+   functions hash_clear(), hash_destroy(), hash_insert(),
+   hash_replace(), or hash_delete(), invalidates all
+   iterators. */
+struct hash_elem *
+hash_next (struct hash_iterator *i)
+{
+  ASSERT (i != NULL);
+
+  i->elem = list_elem_to_hash_elem (list_next (&i->elem->list_elem));
+  while (i->elem == list_elem_to_hash_elem (list_end (i->bucket)))
+    {
+      if (++i->bucket >= i->hash->buckets + i->hash->bucket_cnt)
+        {
+          i->elem = NULL;
+          break;
+        }
+      i->elem = list_elem_to_hash_elem (list_begin (i->bucket));
+    }
+  
+  return i->elem;
+}
+
+/* Returns the current element in the hash table iteration, or a
+   null pointer at the end of the table.  Undefined behavior
+   after calling hash_first() but before hash_next(). */
+struct hash_elem *
+hash_cur (struct hash_iterator *i) 
+{
+  return i->elem;
+}
+
+/* Returns the number of elements in H. */
+size_t
+hash_size (struct hash *h) 
+{
+  return h->elem_cnt;
+}
+
+/* Returns true if H contains no elements, false otherwise. */
+bool
+hash_empty (struct hash *h) 
+{
+  return h->elem_cnt == 0;
+}
+
+/* Fowler-Noll-Vo hash constants, for 32-bit word sizes. */
+#define FNV_32_PRIME 16777619u
+#define FNV_32_BASIS 2166136261u
+
+/* Returns a hash of the SIZE bytes in BUF. */
+unsigned
+hash_bytes (const void *buf_, size_t size)
+{
+  /* Fowler-Noll-Vo 32-bit hash, for bytes. */
+  const unsigned char *buf = buf_;
+  unsigned hash;
+
+  ASSERT (buf != NULL);
+
+  hash = FNV_32_BASIS;
+  while (size-- > 0)
+    hash = (hash * FNV_32_PRIME) ^ *buf++;
+
+  return hash;
+} 
+
+/* Returns a hash of string S. */
+unsigned
+hash_string (const char *s_) 
+{
+  const unsigned char *s = (const unsigned char *) s_;
+  unsigned hash;
+
+  ASSERT (s != NULL);
+
+  hash = FNV_32_BASIS;
+  while (*s != '\0')
+    hash = (hash * FNV_32_PRIME) ^ *s++;
+
+  return hash;
+}
+
+/* Returns a hash of integer I. */
+unsigned
+hash_int (int i) 
+{
+  return hash_bytes (&i, sizeof i);
+}
+
+/* Returns the bucket in H that E belongs in. */
+static struct list *
+find_bucket (struct hash *h, struct hash_elem *e) 
+{
+  size_t bucket_idx = h->hash (e, h->aux) & (h->bucket_cnt - 1);
+  return &h->buckets[bucket_idx];
+}
+
+/* Searches BUCKET in H for a hash element equal to E.  Returns
+   it if found or a null pointer otherwise. */
+static struct hash_elem *
+find_elem (struct hash *h, struct list *bucket, struct hash_elem *e) 
+{
+  struct list_elem *i;
+
+  for (i = list_begin (bucket); i != list_end (bucket); i = list_next (i)) 
+    {
+      struct hash_elem *hi = list_elem_to_hash_elem (i);
+      if (!h->less (hi, e, h->aux) && !h->less (e, hi, h->aux))
+        return hi; 
+    }
+  return NULL;
+}
+
+/* Returns X with its lowest-order bit set to 1 turned off. */
+static inline size_t
+turn_off_least_1bit (size_t x) 
+{
+  return x & (x - 1);
+}
+
+/* Returns true if X is a power of 2, otherwise false. */
+static inline size_t
+is_power_of_2 (size_t x) 
+{
+  return x != 0 && turn_off_least_1bit (x) == 0;
+}
+
+/* Element per bucket ratios. */
+#define MIN_ELEMS_PER_BUCKET  1 /* Elems/bucket < 1: reduce # of buckets. */
+#define BEST_ELEMS_PER_BUCKET 2 /* Ideal elems/bucket. */
+#define MAX_ELEMS_PER_BUCKET  4 /* Elems/bucket > 4: increase # of buckets. */
+
+/* Changes the number of buckets in hash table H to match the
+   ideal.  This function can fail because of an out-of-memory
+   condition, but that'll just make hash accesses less efficient;
+   we can still continue. */
+static void
+rehash (struct hash *h) 
+{
+  size_t old_bucket_cnt, new_bucket_cnt;
+  struct list *new_buckets, *old_buckets;
+  size_t i;
+
+  ASSERT (h != NULL);
+
+  /* Save old bucket info for later use. */
+  old_buckets = h->buckets;
+  old_bucket_cnt = h->bucket_cnt;
+
+  /* Calculate the number of buckets to use now.
+     We want one bucket for about every BEST_ELEMS_PER_BUCKET.
+     We must have at least four buckets, and the number of
+     buckets must be a power of 2. */
+  new_bucket_cnt = h->elem_cnt / BEST_ELEMS_PER_BUCKET;
+  if (new_bucket_cnt < 4)
+    new_bucket_cnt = 4;
+  while (!is_power_of_2 (new_bucket_cnt))
+    new_bucket_cnt = turn_off_least_1bit (new_bucket_cnt);
+
+  /* Don't do anything if the bucket count wouldn't change. */
+  if (new_bucket_cnt == old_bucket_cnt)
+    return;
+
+  /* Allocate new buckets and initialize them as empty. */
+  new_buckets = malloc (sizeof *new_buckets * new_bucket_cnt);
+  if (new_buckets == NULL) 
+    {
+      /* Allocation failed.  This means that use of the hash table will
+         be less efficient.  However, it is still usable, so
+         there's no reason for it to be an error. */
+      return;
+    }
+  for (i = 0; i < new_bucket_cnt; i++) 
+    list_init (&new_buckets[i]);
+
+  /* Install new bucket info. */
+  h->buckets = new_buckets;
+  h->bucket_cnt = new_bucket_cnt;
+
+  /* Move each old element into the appropriate new bucket. */
+  for (i = 0; i < old_bucket_cnt; i++) 
+    {
+      struct list *old_bucket;
+      struct list_elem *elem, *next;
+
+      old_bucket = &old_buckets[i];
+      for (elem = list_begin (old_bucket);
+           elem != list_end (old_bucket); elem = next) 
+        {
+          struct list *new_bucket
+            = find_bucket (h, list_elem_to_hash_elem (elem));
+          next = list_next (elem);
+          list_remove (elem);
+          list_push_front (new_bucket, elem);
+        }
+    }
+
+  free (old_buckets);
+}
+
+/* Inserts E into BUCKET (in hash table H). */
+static void
+insert_elem (struct hash *h, struct list *bucket, struct hash_elem *e) 
+{
+  h->elem_cnt++;
+  list_push_front (bucket, &e->list_elem);
+}
+
+/* Removes E from hash table H. */
+static void
+remove_elem (struct hash *h, struct hash_elem *e) 
+{
+  h->elem_cnt--;
+  list_remove (&e->list_elem);
+}
+
diff --git a/src/lib/kernel/hash.h b/src/lib/kernel/hash.h
new file mode 100644
index 0000000..db9f674
--- /dev/null
+++ b/src/lib/kernel/hash.h
@@ -0,0 +1,103 @@
+#ifndef __LIB_KERNEL_HASH_H
+#define __LIB_KERNEL_HASH_H
+
+/* Hash table.
+
+   This data structure is thoroughly documented in the Tour of
+   Pintos for Project 3.
+
+   This is a standard hash table with chaining.  To locate an
+   element in the table, we compute a hash function over the
+   element's data and use that as an index into an array of
+   doubly linked lists, then linearly search the list.
+
+   The chain lists do not use dynamic allocation.  Instead, each
+   structure that can potentially be in a hash must embed a
+   struct hash_elem member.  All of the hash functions operate on
+   these `struct hash_elem's.  The hash_entry macro allows
+   conversion from a struct hash_elem back to a structure object
+   that contains it.  This is the same technique used in the
+   linked list implementation.  Refer to lib/kernel/list.h for a
+   detailed explanation. */
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+#include "list.h"
+
+/* Hash element. */
+struct hash_elem 
+  {
+    struct list_elem list_elem;
+  };
+
+/* Converts pointer to hash element HASH_ELEM into a pointer to
+   the structure that HASH_ELEM is embedded inside.  Supply the
+   name of the outer structure STRUCT and the member name MEMBER
+   of the hash element.  See the big comment at the top of the
+   file for an example. */
+#define hash_entry(HASH_ELEM, STRUCT, MEMBER)                   \
+        ((STRUCT *) ((uint8_t *) &(HASH_ELEM)->list_elem        \
+                     - offsetof (STRUCT, MEMBER.list_elem)))
+
+/* Computes and returns the hash value for hash element E, given
+   auxiliary data AUX. */
+typedef unsigned hash_hash_func (const struct hash_elem *e, void *aux);
+
+/* Compares the value of two hash elements A and B, given
+   auxiliary data AUX.  Returns true if A is less than B, or
+   false if A is greater than or equal to B. */
+typedef bool hash_less_func (const struct hash_elem *a,
+                             const struct hash_elem *b,
+                             void *aux);
+
+/* Performs some operation on hash element E, given auxiliary
+   data AUX. */
+typedef void hash_action_func (struct hash_elem *e, void *aux);
+
+/* Hash table. */
+struct hash 
+  {
+    size_t elem_cnt;            /* Number of elements in table. */
+    size_t bucket_cnt;          /* Number of buckets, a power of 2. */
+    struct list *buckets;       /* Array of `bucket_cnt' lists. */
+    hash_hash_func *hash;       /* Hash function. */
+    hash_less_func *less;       /* Comparison function. */
+    void *aux;                  /* Auxiliary data for `hash' and `less'. */
+  };
+
+/* A hash table iterator. */
+struct hash_iterator 
+  {
+    struct hash *hash;          /* The hash table. */
+    struct list *bucket;        /* Current bucket. */
+    struct hash_elem *elem;     /* Current hash element in current bucket. */
+  };
+
+/* Basic life cycle. */
+bool hash_init (struct hash *, hash_hash_func *, hash_less_func *, void *aux);
+void hash_clear (struct hash *, hash_action_func *);
+void hash_destroy (struct hash *, hash_action_func *);
+
+/* Search, insertion, deletion. */
+struct hash_elem *hash_insert (struct hash *, struct hash_elem *);
+struct hash_elem *hash_replace (struct hash *, struct hash_elem *);
+struct hash_elem *hash_find (struct hash *, struct hash_elem *);
+struct hash_elem *hash_delete (struct hash *, struct hash_elem *);
+
+/* Iteration. */
+void hash_apply (struct hash *, hash_action_func *);
+void hash_first (struct hash_iterator *, struct hash *);
+struct hash_elem *hash_next (struct hash_iterator *);
+struct hash_elem *hash_cur (struct hash_iterator *);
+
+/* Information. */
+size_t hash_size (struct hash *);
+bool hash_empty (struct hash *);
+
+/* Sample hash functions. */
+unsigned hash_bytes (const void *, size_t);
+unsigned hash_string (const char *);
+unsigned hash_int (int);
+
+#endif /* lib/kernel/hash.h */
diff --git a/src/lib/kernel/list.c b/src/lib/kernel/list.c
new file mode 100644
index 0000000..316d9ef
--- /dev/null
+++ b/src/lib/kernel/list.c
@@ -0,0 +1,524 @@
+#include "list.h"
+#include "../debug.h"
+
+/* Our doubly linked lists have two header elements: the "head"
+   just before the first element and the "tail" just after the
+   last element.  The `prev' link of the front header is null, as
+   is the `next' link of the back header.  Their other two links
+   point toward each other via the interior elements of the list.
+
+   An empty list looks like this:
+
+                      +------+     +------+
+                  <---| head |<--->| tail |--->
+                      +------+     +------+
+
+   A list with two elements in it looks like this:
+
+        +------+     +-------+     +-------+     +------+
+    <---| head |<--->|   1   |<--->|   2   |<--->| tail |<--->
+        +------+     +-------+     +-------+     +------+
+
+   The symmetry of this arrangement eliminates lots of special
+   cases in list processing.  For example, take a look at
+   list_remove(): it takes only two pointer assignments and no
+   conditionals.  That's a lot simpler than the code would be
+   without header elements.
+
+   (Because only one of the pointers in each header element is used,
+   we could in fact combine them into a single header element
+   without sacrificing this simplicity.  But using two separate
+   elements allows us to do a little bit of checking on some
+   operations, which can be valuable.) */
+
+static bool is_sorted (struct list_elem *a, struct list_elem *b,
+                       list_less_func *less, void *aux) UNUSED;
+
+/* Returns true if ELEM is a head, false otherwise. */
+static inline bool
+is_head (struct list_elem *elem)
+{
+  return elem != NULL && elem->prev == NULL && elem->next != NULL;
+}
+
+/* Returns true if ELEM is an interior element,
+   false otherwise. */
+static inline bool
+is_interior (struct list_elem *elem)
+{
+  return elem != NULL && elem->prev != NULL && elem->next != NULL;
+}
+
+/* Returns true if ELEM is a tail, false otherwise. */
+static inline bool
+is_tail (struct list_elem *elem)
+{
+  return elem != NULL && elem->prev != NULL && elem->next == NULL;
+}
+
+/* Initializes LIST as an empty list. */
+void
+list_init (struct list *list)
+{
+  ASSERT (list != NULL);
+  list->head.prev = NULL;
+  list->head.next = &list->tail;
+  list->tail.prev = &list->head;
+  list->tail.next = NULL;
+}
+
+/* Returns the beginning of LIST.  */
+struct list_elem *
+list_begin (struct list *list)
+{
+  ASSERT (list != NULL);
+  return list->head.next;
+}
+
+/* Returns the element after ELEM in its list.  If ELEM is the
+   last element in its list, returns the list tail.  Results are
+   undefined if ELEM is itself a list tail. */
+struct list_elem *
+list_next (struct list_elem *elem)
+{
+  ASSERT (is_head (elem) || is_interior (elem));
+  return elem->next;
+}
+
+/* Returns LIST's tail.
+
+   list_end() is often used in iterating through a list from
+   front to back.  See the big comment at the top of list.h for
+   an example. */
+struct list_elem *
+list_end (struct list *list)
+{
+  ASSERT (list != NULL);
+  return &list->tail;
+}
+
+/* Returns the LIST's reverse beginning, for iterating through
+   LIST in reverse order, from back to front. */
+struct list_elem *
+list_rbegin (struct list *list) 
+{
+  ASSERT (list != NULL);
+  return list->tail.prev;
+}
+
+/* Returns the element before ELEM in its list.  If ELEM is the
+   first element in its list, returns the list head.  Results are
+   undefined if ELEM is itself a list head. */
+struct list_elem *
+list_prev (struct list_elem *elem)
+{
+  ASSERT (is_interior (elem) || is_tail (elem));
+  return elem->prev;
+}
+
+/* Returns LIST's head.
+
+   list_rend() is often used in iterating through a list in
+   reverse order, from back to front.  Here's typical usage,
+   following the example from the top of list.h:
+
+      for (e = list_rbegin (&foo_list); e != list_rend (&foo_list);
+           e = list_prev (e))
+        {
+          struct foo *f = list_entry (e, struct foo, elem);
+          ...do something with f...
+        }
+*/
+struct list_elem *
+list_rend (struct list *list) 
+{
+  ASSERT (list != NULL);
+  return &list->head;
+}
+
+/* Return's LIST's head.
+
+   list_head() can be used for an alternate style of iterating
+   through a list, e.g.:
+
+      e = list_head (&list);
+      while ((e = list_next (e)) != list_end (&list)) 
+        {
+          ...
+        }
+*/
+struct list_elem *
+list_head (struct list *list) 
+{
+  ASSERT (list != NULL);
+  return &list->head;
+}
+
+/* Return's LIST's tail. */
+struct list_elem *
+list_tail (struct list *list) 
+{
+  ASSERT (list != NULL);
+  return &list->tail;
+}
+
+/* Inserts ELEM just before BEFORE, which may be either an
+   interior element or a tail.  The latter case is equivalent to
+   list_push_back(). */
+void
+list_insert (struct list_elem *before, struct list_elem *elem)
+{
+  ASSERT (is_interior (before) || is_tail (before));
+  ASSERT (elem != NULL);
+
+  elem->prev = before->prev;
+  elem->next = before;
+  before->prev->next = elem;
+  before->prev = elem;
+}
+
+/* Removes elements FIRST though LAST (exclusive) from their
+   current list, then inserts them just before BEFORE, which may
+   be either an interior element or a tail. */
+void
+list_splice (struct list_elem *before,
+             struct list_elem *first, struct list_elem *last)
+{
+  ASSERT (is_interior (before) || is_tail (before));
+  if (first == last)
+    return;
+  last = list_prev (last);
+
+  ASSERT (is_interior (first));
+  ASSERT (is_interior (last));
+
+  /* Cleanly remove FIRST...LAST from its current list. */
+  first->prev->next = last->next;
+  last->next->prev = first->prev;
+
+  /* Splice FIRST...LAST into new list. */
+  first->prev = before->prev;
+  last->next = before;
+  before->prev->next = first;
+  before->prev = last;
+}
+
+/* Inserts ELEM at the beginning of LIST, so that it becomes the
+   front in LIST. */
+void
+list_push_front (struct list *list, struct list_elem *elem)
+{
+  list_insert (list_begin (list), elem);
+}
+
+/* Inserts ELEM at the end of LIST, so that it becomes the
+   back in LIST. */
+void
+list_push_back (struct list *list, struct list_elem *elem)
+{
+  list_insert (list_end (list), elem);
+}
+
+/* Removes ELEM from its list and returns the element that
+   followed it.  Undefined behavior if ELEM is not in a list.
+
+   A list element must be treated very carefully after removing
+   it from its list.  Calling list_next() or list_prev() on ELEM
+   will return the item that was previously before or after ELEM,
+   but, e.g., list_prev(list_next(ELEM)) is no longer ELEM!
+
+   The list_remove() return value provides a convenient way to
+   iterate and remove elements from a list:
+
+   for (e = list_begin (&list); e != list_end (&list); e = list_remove (e))
+     {
+       ...do something with e...
+     }
+
+   If you need to free() elements of the list then you need to be
+   more conservative.  Here's an alternate strategy that works
+   even in that case:
+
+   while (!list_empty (&list))
+     {
+       struct list_elem *e = list_pop_front (&list);
+       ...do something with e...
+     }
+*/
+struct list_elem *
+list_remove (struct list_elem *elem)
+{
+  ASSERT (is_interior (elem));
+  elem->prev->next = elem->next;
+  elem->next->prev = elem->prev;
+  return elem->next;
+}
+
+/* Removes the front element from LIST and returns it.
+   Undefined behavior if LIST is empty before removal. */
+struct list_elem *
+list_pop_front (struct list *list)
+{
+  struct list_elem *front = list_front (list);
+  list_remove (front);
+  return front;
+}
+
+/* Removes the back element from LIST and returns it.
+   Undefined behavior if LIST is empty before removal. */
+struct list_elem *
+list_pop_back (struct list *list)
+{
+  struct list_elem *back = list_back (list);
+  list_remove (back);
+  return back;
+}
+
+/* Returns the front element in LIST.
+   Undefined behavior if LIST is empty. */
+struct list_elem *
+list_front (struct list *list)
+{
+  ASSERT (!list_empty (list));
+  return list->head.next;
+}
+
+/* Returns the back element in LIST.
+   Undefined behavior if LIST is empty. */
+struct list_elem *
+list_back (struct list *list)
+{
+  ASSERT (!list_empty (list));
+  return list->tail.prev;
+}
+
+/* Returns the number of elements in LIST.
+   Runs in O(n) in the number of elements. */
+size_t
+list_size (struct list *list)
+{
+  struct list_elem *e;
+  size_t cnt = 0;
+
+  for (e = list_begin (list); e != list_end (list); e = list_next (e))
+    cnt++;
+  return cnt;
+}
+
+/* Returns true if LIST is empty, false otherwise. */
+bool
+list_empty (struct list *list)
+{
+  return list_begin (list) == list_end (list);
+}
+
+/* Swaps the `struct list_elem *'s that A and B point to. */
+static void
+swap (struct list_elem **a, struct list_elem **b) 
+{
+  struct list_elem *t = *a;
+  *a = *b;
+  *b = t;
+}
+
+/* Reverses the order of LIST. */
+void
+list_reverse (struct list *list)
+{
+  if (!list_empty (list)) 
+    {
+      struct list_elem *e;
+
+      for (e = list_begin (list); e != list_end (list); e = e->prev)
+        swap (&e->prev, &e->next);
+      swap (&list->head.next, &list->tail.prev);
+      swap (&list->head.next->prev, &list->tail.prev->next);
+    }
+}
+
+/* Returns true only if the list elements A through B (exclusive)
+   are in order according to LESS given auxiliary data AUX. */
+static bool
+is_sorted (struct list_elem *a, struct list_elem *b,
+           list_less_func *less, void *aux)
+{
+  if (a != b)
+    while ((a = list_next (a)) != b) 
+      if (less (a, list_prev (a), aux))
+        return false;
+  return true;
+}
+
+/* Finds a run, starting at A and ending not after B, of list
+   elements that are in nondecreasing order according to LESS
+   given auxiliary data AUX.  Returns the (exclusive) end of the
+   run.
+   A through B (exclusive) must form a non-empty range. */
+static struct list_elem *
+find_end_of_run (struct list_elem *a, struct list_elem *b,
+                 list_less_func *less, void *aux)
+{
+  ASSERT (a != NULL);
+  ASSERT (b != NULL);
+  ASSERT (less != NULL);
+  ASSERT (a != b);
+  
+  do 
+    {
+      a = list_next (a);
+    }
+  while (a != b && !less (a, list_prev (a), aux));
+  return a;
+}
+
+/* Merges A0 through A1B0 (exclusive) with A1B0 through B1
+   (exclusive) to form a combined range also ending at B1
+   (exclusive).  Both input ranges must be nonempty and sorted in
+   nondecreasing order according to LESS given auxiliary data
+   AUX.  The output range will be sorted the same way. */
+static void
+inplace_merge (struct list_elem *a0, struct list_elem *a1b0,
+               struct list_elem *b1,
+               list_less_func *less, void *aux)
+{
+  ASSERT (a0 != NULL);
+  ASSERT (a1b0 != NULL);
+  ASSERT (b1 != NULL);
+  ASSERT (less != NULL);
+  ASSERT (is_sorted (a0, a1b0, less, aux));
+  ASSERT (is_sorted (a1b0, b1, less, aux));
+
+  while (a0 != a1b0 && a1b0 != b1)
+    if (!less (a1b0, a0, aux)) 
+      a0 = list_next (a0);
+    else 
+      {
+        a1b0 = list_next (a1b0);
+        list_splice (a0, list_prev (a1b0), a1b0);
+      }
+}
+
+/* Sorts LIST according to LESS given auxiliary data AUX, using a
+   natural iterative merge sort that runs in O(n lg n) time and
+   O(1) space in the number of elements in LIST. */
+void
+list_sort (struct list *list, list_less_func *less, void *aux)
+{
+  size_t output_run_cnt;        /* Number of runs output in current pass. */
+
+  ASSERT (list != NULL);
+  ASSERT (less != NULL);
+
+  /* Pass over the list repeatedly, merging adjacent runs of
+     nondecreasing elements, until only one run is left. */
+  do
+    {
+      struct list_elem *a0;     /* Start of first run. */
+      struct list_elem *a1b0;   /* End of first run, start of second. */
+      struct list_elem *b1;     /* End of second run. */
+
+      output_run_cnt = 0;
+      for (a0 = list_begin (list); a0 != list_end (list); a0 = b1)
+        {
+          /* Each iteration produces one output run. */
+          output_run_cnt++;
+
+          /* Locate two adjacent runs of nondecreasing elements
+             A0...A1B0 and A1B0...B1. */
+          a1b0 = find_end_of_run (a0, list_end (list), less, aux);
+          if (a1b0 == list_end (list))
+            break;
+          b1 = find_end_of_run (a1b0, list_end (list), less, aux);
+
+          /* Merge the runs. */
+          inplace_merge (a0, a1b0, b1, less, aux);
+        }
+    }
+  while (output_run_cnt > 1);
+
+  ASSERT (is_sorted (list_begin (list), list_end (list), less, aux));
+}
+
+/* Inserts ELEM in the proper position in LIST, which must be
+   sorted according to LESS given auxiliary data AUX.
+   Runs in O(n) average case in the number of elements in LIST. */
+void
+list_insert_ordered (struct list *list, struct list_elem *elem,
+                     list_less_func *less, void *aux)
+{
+  struct list_elem *e;
+
+  ASSERT (list != NULL);
+  ASSERT (elem != NULL);
+  ASSERT (less != NULL);
+
+  for (e = list_begin (list); e != list_end (list); e = list_next (e))
+    if (less (elem, e, aux))
+      break;
+  return list_insert (e, elem);
+}
+
+/* Iterates through LIST and removes all but the first in each
+   set of adjacent elements that are equal according to LESS
+   given auxiliary data AUX.  If DUPLICATES is non-null, then the
+   elements from LIST are appended to DUPLICATES. */
+void
+list_unique (struct list *list, struct list *duplicates,
+             list_less_func *less, void *aux)
+{
+  struct list_elem *elem, *next;
+
+  ASSERT (list != NULL);
+  ASSERT (less != NULL);
+  if (list_empty (list))
+    return;
+
+  elem = list_begin (list);
+  while ((next = list_next (elem)) != list_end (list))
+    if (!less (elem, next, aux) && !less (next, elem, aux)) 
+      {
+        list_remove (next);
+        if (duplicates != NULL)
+          list_push_back (duplicates, next);
+      }
+    else
+      elem = next;
+}
+
+/* Returns the element in LIST with the largest value according
+   to LESS given auxiliary data AUX.  If there is more than one
+   maximum, returns the one that appears earlier in the list.  If
+   the list is empty, returns its tail. */
+struct list_elem *
+list_max (struct list *list, list_less_func *less, void *aux)
+{
+  struct list_elem *max = list_begin (list);
+  if (max != list_end (list)) 
+    {
+      struct list_elem *e;
+      
+      for (e = list_next (max); e != list_end (list); e = list_next (e))
+        if (less (max, e, aux))
+          max = e; 
+    }
+  return max;
+}
+
+/* Returns the element in LIST with the smallest value according
+   to LESS given auxiliary data AUX.  If there is more than one
+   minimum, returns the one that appears earlier in the list.  If
+   the list is empty, returns its tail. */
+struct list_elem *
+list_min (struct list *list, list_less_func *less, void *aux)
+{
+  struct list_elem *min = list_begin (list);
+  if (min != list_end (list)) 
+    {
+      struct list_elem *e;
+      
+      for (e = list_next (min); e != list_end (list); e = list_next (e))
+        if (less (e, min, aux))
+          min = e; 
+    }
+  return min;
+}
diff --git a/src/lib/kernel/list.h b/src/lib/kernel/list.h
new file mode 100644
index 0000000..82efbb5
--- /dev/null
+++ b/src/lib/kernel/list.h
@@ -0,0 +1,181 @@
+#ifndef __LIB_KERNEL_LIST_H
+#define __LIB_KERNEL_LIST_H
+
+/* Doubly linked list.
+
+   This implementation of a doubly linked list does not require
+   use of dynamically allocated memory.  Instead, each structure
+   that is a potential list element must embed a struct list_elem
+   member.  All of the list functions operate on these `struct
+   list_elem's.  The list_entry macro allows conversion from a
+   struct list_elem back to a structure object that contains it.
+
+   For example, suppose there is a needed for a list of `struct
+   foo'.  `struct foo' should contain a `struct list_elem'
+   member, like so:
+
+      struct foo
+        {
+          struct list_elem elem;
+          int bar;
+          ...other members...
+        };
+
+   Then a list of `struct foo' can be be declared and initialized
+   like so:
+
+      struct list foo_list;
+
+      list_init (&foo_list);
+
+   Iteration is a typical situation where it is necessary to
+   convert from a struct list_elem back to its enclosing
+   structure.  Here's an example using foo_list:
+
+      struct list_elem *e;
+
+      for (e = list_begin (&foo_list); e != list_end (&foo_list);
+           e = list_next (e))
+        {
+          struct foo *f = list_entry (e, struct foo, elem);
+          ...do something with f...
+        }
+
+   You can find real examples of list usage throughout the
+   source; for example, malloc.c, palloc.c, and thread.c in the
+   threads directory all use lists.
+
+   The interface for this list is inspired by the list<> template
+   in the C++ STL.  If you're familiar with list<>, you should
+   find this easy to use.  However, it should be emphasized that
+   these lists do *no* type checking and can't do much other
+   correctness checking.  If you screw up, it will bite you.
+
+   Glossary of list terms:
+
+     - "front": The first element in a list.  Undefined in an
+       empty list.  Returned by list_front().
+
+     - "back": The last element in a list.  Undefined in an empty
+       list.  Returned by list_back().
+
+     - "tail": The element figuratively just after the last
+       element of a list.  Well defined even in an empty list.
+       Returned by list_end().  Used as the end sentinel for an
+       iteration from front to back.
+
+     - "beginning": In a non-empty list, the front.  In an empty
+       list, the tail.  Returned by list_begin().  Used as the
+       starting point for an iteration from front to back.
+
+     - "head": The element figuratively just before the first
+       element of a list.  Well defined even in an empty list.
+       Returned by list_rend().  Used as the end sentinel for an
+       iteration from back to front.
+
+     - "reverse beginning": In a non-empty list, the back.  In an
+       empty list, the head.  Returned by list_rbegin().  Used as
+       the starting point for an iteration from back to front.
+
+     - "interior element": An element that is not the head or
+       tail, that is, a real list element.  An empty list does
+       not have any interior elements.
+*/
+
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/* List element. */
+struct list_elem 
+  {
+    struct list_elem *prev;     /* Previous list element. */
+    struct list_elem *next;     /* Next list element. */
+  };
+
+/* List. */
+struct list 
+  {
+    struct list_elem head;      /* List head. */
+    struct list_elem tail;      /* List tail. */
+  };
+
+/* Converts pointer to list element LIST_ELEM into a pointer to
+   the structure that LIST_ELEM is embedded inside.  Supply the
+   name of the outer structure STRUCT and the member name MEMBER
+   of the list element.  See the big comment at the top of the
+   file for an example. */
+#define list_entry(LIST_ELEM, STRUCT, MEMBER)           \
+        ((STRUCT *) ((uint8_t *) &(LIST_ELEM)->next     \
+                     - offsetof (STRUCT, MEMBER.next)))
+
+/* List initialization.
+
+   A list may be initialized by calling list_init():
+
+       struct list my_list;
+       list_init (&my_list);
+
+   or with an initializer using LIST_INITIALIZER:
+
+       struct list my_list = LIST_INITIALIZER (my_list); */
+#define LIST_INITIALIZER(NAME) { { NULL, &(NAME).tail }, \
+                                 { &(NAME).head, NULL } }
+
+void list_init (struct list *);
+
+/* List traversal. */
+struct list_elem *list_begin (struct list *);
+struct list_elem *list_next (struct list_elem *);
+struct list_elem *list_end (struct list *);
+
+struct list_elem *list_rbegin (struct list *);
+struct list_elem *list_prev (struct list_elem *);
+struct list_elem *list_rend (struct list *);
+
+struct list_elem *list_head (struct list *);
+struct list_elem *list_tail (struct list *);
+
+/* List insertion. */
+void list_insert (struct list_elem *, struct list_elem *);
+void list_splice (struct list_elem *before,
+                  struct list_elem *first, struct list_elem *last);
+void list_push_front (struct list *, struct list_elem *);
+void list_push_back (struct list *, struct list_elem *);
+
+/* List removal. */
+struct list_elem *list_remove (struct list_elem *);
+struct list_elem *list_pop_front (struct list *);
+struct list_elem *list_pop_back (struct list *);
+
+/* List elements. */
+struct list_elem *list_front (struct list *);
+struct list_elem *list_back (struct list *);
+
+/* List properties. */
+size_t list_size (struct list *);
+bool list_empty (struct list *);
+
+/* Miscellaneous. */
+void list_reverse (struct list *);
+
+/* Compares the value of two list elements A and B, given
+   auxiliary data AUX.  Returns true if A is less than B, or
+   false if A is greater than or equal to B. */
+typedef bool list_less_func (const struct list_elem *a,
+                             const struct list_elem *b,
+                             void *aux);
+
+/* Operations on lists with ordered elements. */
+void list_sort (struct list *,
+                list_less_func *, void *aux);
+void list_insert_ordered (struct list *, struct list_elem *,
+                          list_less_func *, void *aux);
+void list_unique (struct list *, struct list *duplicates,
+                  list_less_func *, void *aux);
+
+/* Max and min. */
+struct list_elem *list_max (struct list *, list_less_func *, void *aux);
+struct list_elem *list_min (struct list *, list_less_func *, void *aux);
+
+#endif /* lib/kernel/list.h */
diff --git a/src/lib/kernel/stdio.h b/src/lib/kernel/stdio.h
new file mode 100644
index 0000000..3e5bae9
--- /dev/null
+++ b/src/lib/kernel/stdio.h
@@ -0,0 +1,6 @@
+#ifndef __LIB_KERNEL_STDIO_H
+#define __LIB_KERNEL_STDIO_H
+
+void putbuf (const char *, size_t);
+
+#endif /* lib/kernel/stdio.h */
diff --git a/src/lib/limits.h b/src/lib/limits.h
new file mode 100644
index 0000000..c957ec4
--- /dev/null
+++ b/src/lib/limits.h
@@ -0,0 +1,34 @@
+#ifndef __LIB_LIMITS_H
+#define __LIB_LIMITS_H
+
+#define CHAR_BIT 8
+
+#define SCHAR_MAX 127
+#define SCHAR_MIN (-SCHAR_MAX - 1)
+#define UCHAR_MAX 255
+
+#ifdef __CHAR_UNSIGNED__
+#define CHAR_MIN 0
+#define CHAR_MAX UCHAR_MAX
+#else
+#define CHAR_MIN SCHAR_MIN
+#define CHAR_MAX SCHAR_MAX
+#endif
+
+#define SHRT_MAX 32767
+#define SHRT_MIN (-SHRT_MAX - 1)
+#define USHRT_MAX 65535
+
+#define INT_MAX 2147483647
+#define INT_MIN (-INT_MAX - 1)
+#define UINT_MAX 4294967295U
+
+#define LONG_MAX 2147483647L
+#define LONG_MIN (-LONG_MAX - 1)
+#define ULONG_MAX 4294967295UL
+
+#define LLONG_MAX 9223372036854775807LL
+#define LLONG_MIN (-LLONG_MAX - 1)
+#define ULLONG_MAX 18446744073709551615ULL
+
+#endif /* lib/limits.h */
diff --git a/src/lib/packed.h b/src/lib/packed.h
new file mode 100644
index 0000000..9a9b6e2
--- /dev/null
+++ b/src/lib/packed.h
@@ -0,0 +1,10 @@
+#ifndef __LIB_PACKED_H
+#define __LIB_PACKED_H
+
+/* The "packed" attribute, when applied to a structure, prevents
+   GCC from inserting padding bytes between or after structure
+   members.  It must be specified at the time of the structure's
+   definition, normally just after the closing brace. */
+#define PACKED __attribute__ ((packed))
+
+#endif /* lib/packed.h */
diff --git a/src/lib/random.c b/src/lib/random.c
new file mode 100644
index 0000000..6a963e2
--- /dev/null
+++ b/src/lib/random.c
@@ -0,0 +1,86 @@
+#include "random.h"
+#include <stdbool.h>
+#include <stdint.h>
+#include "debug.h"
+
+/* RC4-based pseudo-random number generator (PRNG).
+
+   RC4 is a stream cipher.  We're not using it here for its
+   cryptographic properties, but because it is easy to implement
+   and its output is plenty random for non-cryptographic
+   purposes.
+
+   See http://en.wikipedia.org/wiki/RC4_(cipher) for information
+   on RC4.*/
+
+/* RC4 state. */
+static uint8_t s[256];          /* S[]. */
+static uint8_t s_i, s_j;        /* i, j. */
+
+/* Already initialized? */
+static bool inited;     
+
+/* Swaps the bytes pointed to by A and B. */
+static inline void
+swap_byte (uint8_t *a, uint8_t *b) 
+{
+  uint8_t t = *a;
+  *a = *b;
+  *b = t;
+}
+
+/* Initializes or reinitializes the PRNG with the given SEED. */
+void
+random_init (unsigned seed)
+{
+  uint8_t *seedp = (uint8_t *) &seed;
+  int i;
+  uint8_t j;
+
+  if (inited)
+    return;
+
+  for (i = 0; i < 256; i++) 
+    s[i] = i;
+  for (i = j = 0; i < 256; i++) 
+    {
+      j += s[i] + seedp[i % sizeof seed];
+      swap_byte (s + i, s + j);
+    }
+
+  s_i = s_j = 0;
+  inited = true;
+}
+
+/* Writes SIZE random bytes into BUF. */
+void
+random_bytes (void *buf_, size_t size) 
+{
+  uint8_t *buf;
+
+  if (!inited)
+    random_init (0);
+
+  for (buf = buf_; size-- > 0; buf++)
+    {
+      uint8_t s_k;
+      
+      s_i++;
+      s_j += s[s_i];
+      swap_byte (s + s_i, s + s_j);
+
+      s_k = s[s_i] + s[s_j];
+      *buf = s[s_k];
+    }
+}
+
+/* Returns a pseudo-random unsigned long.
+   Use random_ulong() % n to obtain a random number in the range
+   0...n (exclusive). */
+unsigned long
+random_ulong (void) 
+{
+  unsigned long ul;
+  random_bytes (&ul, sizeof ul);
+  return ul;
+}
diff --git a/src/lib/random.h b/src/lib/random.h
new file mode 100644
index 0000000..0950ae2
--- /dev/null
+++ b/src/lib/random.h
@@ -0,0 +1,10 @@
+#ifndef __LIB_RANDOM_H
+#define __LIB_RANDOM_H
+
+#include <stddef.h>
+
+void random_init (unsigned seed);
+void random_bytes (void *, size_t);
+unsigned long random_ulong (void);
+
+#endif /* lib/random.h */
diff --git a/src/lib/round.h b/src/lib/round.h
new file mode 100644
index 0000000..3aa6642
--- /dev/null
+++ b/src/lib/round.h
@@ -0,0 +1,18 @@
+#ifndef __LIB_ROUND_H
+#define __LIB_ROUND_H
+
+/* Yields X rounded up to the nearest multiple of STEP.
+   For X >= 0, STEP >= 1 only. */
+#define ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP) * (STEP))
+
+/* Yields X divided by STEP, rounded up.
+   For X >= 0, STEP >= 1 only. */
+#define DIV_ROUND_UP(X, STEP) (((X) + (STEP) - 1) / (STEP))
+
+/* Yields X rounded down to the nearest multiple of STEP.
+   For X >= 0, STEP >= 1 only. */
+#define ROUND_DOWN(X, STEP) ((X) / (STEP) * (STEP))
+
+/* There is no DIV_ROUND_DOWN.   It would be simply X / STEP. */
+
+#endif /* lib/round.h */
diff --git a/src/lib/stdarg.h b/src/lib/stdarg.h
new file mode 100644
index 0000000..32622b5
--- /dev/null
+++ b/src/lib/stdarg.h
@@ -0,0 +1,14 @@
+#ifndef __LIB_STDARG_H
+#define __LIB_STDARG_H
+
+/* GCC has <stdarg.h> functionality as built-ins,
+   so all we need is to use it. */
+
+typedef __builtin_va_list va_list;
+
+#define va_start(LIST, ARG)	__builtin_va_start (LIST, ARG)
+#define va_end(LIST)            __builtin_va_end (LIST)
+#define va_arg(LIST, TYPE)	__builtin_va_arg (LIST, TYPE)
+#define va_copy(DST, SRC)	__builtin_va_copy (DST, SRC)
+
+#endif /* lib/stdarg.h */
diff --git a/src/lib/stdbool.h b/src/lib/stdbool.h
new file mode 100644
index 0000000..f173a91
--- /dev/null
+++ b/src/lib/stdbool.h
@@ -0,0 +1,9 @@
+#ifndef __LIB_STDBOOL_H
+#define __LIB_STDBOOL_H
+
+#define bool	_Bool
+#define true	1
+#define false	0
+#define __bool_true_false_are_defined	1
+
+#endif /* lib/stdbool.h */
diff --git a/src/lib/stddef.h b/src/lib/stddef.h
new file mode 100644
index 0000000..4e74fa6
--- /dev/null
+++ b/src/lib/stddef.h
@@ -0,0 +1,12 @@
+#ifndef __LIB_STDDEF_H
+#define __LIB_STDDEF_H
+
+#define NULL ((void *) 0)
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *) 0)->MEMBER)
+
+/* GCC predefines the types we need for ptrdiff_t and size_t,
+   so that we don't have to guess. */
+typedef __PTRDIFF_TYPE__ ptrdiff_t;
+typedef __SIZE_TYPE__ size_t;
+
+#endif /* lib/stddef.h */
diff --git a/src/lib/stdint.h b/src/lib/stdint.h
new file mode 100644
index 0000000..ef5f214
--- /dev/null
+++ b/src/lib/stdint.h
@@ -0,0 +1,51 @@
+#ifndef __LIB_STDINT_H
+#define __LIB_STDINT_H
+
+typedef signed char int8_t;
+#define INT8_MAX 127
+#define INT8_MIN (-INT8_MAX - 1)
+
+typedef signed short int int16_t;
+#define INT16_MAX 32767
+#define INT16_MIN (-INT16_MAX - 1)
+
+typedef signed int int32_t;
+#define INT32_MAX 2147483647
+#define INT32_MIN (-INT32_MAX - 1)
+
+typedef signed long long int int64_t;
+#define INT64_MAX 9223372036854775807LL
+#define INT64_MIN (-INT64_MAX - 1)
+
+typedef unsigned char uint8_t;
+#define UINT8_MAX 255
+
+typedef unsigned short int uint16_t;
+#define UINT16_MAX 65535
+
+typedef unsigned int uint32_t;
+#define UINT32_MAX 4294967295U
+
+typedef unsigned long long int uint64_t;
+#define UINT64_MAX 18446744073709551615ULL
+
+typedef int32_t intptr_t;
+#define INTPTR_MIN INT32_MIN
+#define INTPTR_MAX INT32_MAX
+
+typedef uint32_t uintptr_t;
+#define UINTPTR_MAX UINT32_MAX
+
+typedef int64_t intmax_t;
+#define INTMAX_MIN INT64_MIN
+#define INTMAX_MAX INT64_MAX
+
+typedef uint64_t uintmax_t;
+#define UINTMAX_MAX UINT64_MAX
+
+#define PTRDIFF_MIN INT32_MIN
+#define PTRDIFF_MAX INT32_MAX
+
+#define SIZE_MAX UINT32_MAX
+
+#endif /* lib/stdint.h */
diff --git a/src/lib/stdio.c b/src/lib/stdio.c
new file mode 100644
index 0000000..8927c50
--- /dev/null
+++ b/src/lib/stdio.c
@@ -0,0 +1,655 @@
+#include <stdio.h>
+#include <ctype.h>
+#include <inttypes.h>
+#include <round.h>
+#include <stdint.h>
+#include <string.h>
+
+/* Auxiliary data for vsnprintf_helper(). */
+struct vsnprintf_aux 
+  {
+    char *p;            /* Current output position. */
+    int length;         /* Length of output string. */
+    int max_length;     /* Max length of output string. */
+  };
+
+static void vsnprintf_helper (char, void *);
+
+/* Like vprintf(), except that output is stored into BUFFER,
+   which must have space for BUF_SIZE characters.  Writes at most
+   BUF_SIZE - 1 characters to BUFFER, followed by a null
+   terminator.  BUFFER will always be null-terminated unless
+   BUF_SIZE is zero.  Returns the number of characters that would
+   have been written to BUFFER, not including a null terminator,
+   had there been enough room. */
+int
+vsnprintf (char *buffer, size_t buf_size, const char *format, va_list args) 
+{
+  /* Set up aux data for vsnprintf_helper(). */
+  struct vsnprintf_aux aux;
+  aux.p = buffer;
+  aux.length = 0;
+  aux.max_length = buf_size > 0 ? buf_size - 1 : 0;
+
+  /* Do most of the work. */
+  __vprintf (format, args, vsnprintf_helper, &aux);
+
+  /* Add null terminator. */
+  if (buf_size > 0)
+    *aux.p = '\0';
+
+  return aux.length;
+}
+
+/* Helper function for vsnprintf(). */
+static void
+vsnprintf_helper (char ch, void *aux_)
+{
+  struct vsnprintf_aux *aux = aux_;
+
+  if (aux->length++ < aux->max_length)
+    *aux->p++ = ch;
+}
+
+/* Like printf(), except that output is stored into BUFFER,
+   which must have space for BUF_SIZE characters.  Writes at most
+   BUF_SIZE - 1 characters to BUFFER, followed by a null
+   terminator.  BUFFER will always be null-terminated unless
+   BUF_SIZE is zero.  Returns the number of characters that would
+   have been written to BUFFER, not including a null terminator,
+   had there been enough room. */
+int
+snprintf (char *buffer, size_t buf_size, const char *format, ...) 
+{
+  va_list args;
+  int retval;
+
+  va_start (args, format);
+  retval = vsnprintf (buffer, buf_size, format, args);
+  va_end (args);
+
+  return retval;
+}
+
+/* Writes formatted output to the console.
+   In the kernel, the console is both the video display and first
+   serial port.
+   In userspace, the console is file descriptor 1. */
+int
+printf (const char *format, ...) 
+{
+  va_list args;
+  int retval;
+
+  va_start (args, format);
+  retval = vprintf (format, args);
+  va_end (args);
+
+  return retval;
+}
+
+/* printf() formatting internals. */
+
+/* A printf() conversion. */
+struct printf_conversion 
+  {
+    /* Flags. */
+    enum 
+      {
+        MINUS = 1 << 0,         /* '-' */
+        PLUS = 1 << 1,          /* '+' */
+        SPACE = 1 << 2,         /* ' ' */
+        POUND = 1 << 3,         /* '#' */
+        ZERO = 1 << 4,          /* '0' */
+        GROUP = 1 << 5          /* '\'' */
+      }
+    flags;
+
+    /* Minimum field width. */
+    int width;
+
+    /* Numeric precision.
+       -1 indicates no precision was specified. */
+    int precision;
+
+    /* Type of argument to format. */
+    enum 
+      {
+        CHAR = 1,               /* hh */
+        SHORT = 2,              /* h */
+        INT = 3,                /* (none) */
+        INTMAX = 4,             /* j */
+        LONG = 5,               /* l */
+        LONGLONG = 6,           /* ll */
+        PTRDIFFT = 7,           /* t */
+        SIZET = 8               /* z */
+      }
+    type;
+  };
+
+struct integer_base 
+  {
+    int base;                   /* Base. */
+    const char *digits;         /* Collection of digits. */
+    int x;                      /* `x' character to use, for base 16 only. */
+    int group;                  /* Number of digits to group with ' flag. */
+  };
+
+static const struct integer_base base_d = {10, "0123456789", 0, 3};
+static const struct integer_base base_o = {8, "01234567", 0, 3};
+static const struct integer_base base_x = {16, "0123456789abcdef", 'x', 4};
+static const struct integer_base base_X = {16, "0123456789ABCDEF", 'X', 4};
+
+static const char *parse_conversion (const char *format,
+                                     struct printf_conversion *,
+                                     va_list *);
+static void format_integer (uintmax_t value, bool is_signed, bool negative, 
+                            const struct integer_base *,
+                            const struct printf_conversion *,
+                            void (*output) (char, void *), void *aux);
+static void output_dup (char ch, size_t cnt,
+                        void (*output) (char, void *), void *aux);
+static void format_string (const char *string, int length,
+                           struct printf_conversion *,
+                           void (*output) (char, void *), void *aux);
+
+void
+__vprintf (const char *format, va_list args,
+           void (*output) (char, void *), void *aux)
+{
+  for (; *format != '\0'; format++)
+    {
+      struct printf_conversion c;
+
+      /* Literally copy non-conversions to output. */
+      if (*format != '%') 
+        {
+          output (*format, aux);
+          continue;
+        }
+      format++;
+
+      /* %% => %. */
+      if (*format == '%') 
+        {
+          output ('%', aux);
+          continue;
+        }
+
+      /* Parse conversion specifiers. */
+      format = parse_conversion (format, &c, &args);
+
+      /* Do conversion. */
+      switch (*format) 
+        {
+        case 'd':
+        case 'i': 
+          {
+            /* Signed integer conversions. */
+            intmax_t value;
+            
+            switch (c.type) 
+              {
+              case CHAR: 
+                value = (signed char) va_arg (args, int);
+                break;
+              case SHORT:
+                value = (short) va_arg (args, int);
+                break;
+              case INT:
+                value = va_arg (args, int);
+                break;
+              case INTMAX:
+                value = va_arg (args, intmax_t);
+                break;
+              case LONG:
+                value = va_arg (args, long);
+                break;
+              case LONGLONG:
+                value = va_arg (args, long long);
+                break;
+              case PTRDIFFT:
+                value = va_arg (args, ptrdiff_t);
+                break;
+              case SIZET:
+                value = va_arg (args, size_t);
+                if (value > SIZE_MAX / 2)
+                  value = value - SIZE_MAX - 1;
+                break;
+              default:
+                NOT_REACHED ();
+              }
+
+            format_integer (value < 0 ? -value : value,
+                            true, value < 0, &base_d, &c, output, aux);
+          }
+          break;
+          
+        case 'o':
+        case 'u':
+        case 'x':
+        case 'X':
+          {
+            /* Unsigned integer conversions. */
+            uintmax_t value;
+            const struct integer_base *b;
+
+            switch (c.type) 
+              {
+              case CHAR: 
+                value = (unsigned char) va_arg (args, unsigned);
+                break;
+              case SHORT:
+                value = (unsigned short) va_arg (args, unsigned);
+                break;
+              case INT:
+                value = va_arg (args, unsigned);
+                break;
+              case INTMAX:
+                value = va_arg (args, uintmax_t);
+                break;
+              case LONG:
+                value = va_arg (args, unsigned long);
+                break;
+              case LONGLONG:
+                value = va_arg (args, unsigned long long);
+                break;
+              case PTRDIFFT:
+                value = va_arg (args, ptrdiff_t);
+#if UINTMAX_MAX != PTRDIFF_MAX
+                value &= ((uintmax_t) PTRDIFF_MAX << 1) | 1;
+#endif
+                break;
+              case SIZET:
+                value = va_arg (args, size_t);
+                break;
+              default:
+                NOT_REACHED ();
+              }
+
+            switch (*format) 
+              {
+              case 'o': b = &base_o; break;
+              case 'u': b = &base_d; break;
+              case 'x': b = &base_x; break;
+              case 'X': b = &base_X; break;
+              default: NOT_REACHED ();
+              }
+
+            format_integer (value, false, false, b, &c, output, aux);
+          }
+          break;
+
+        case 'c': 
+          {
+            /* Treat character as single-character string. */
+            char ch = va_arg (args, int);
+            format_string (&ch, 1, &c, output, aux);
+          }
+          break;
+
+        case 's':
+          {
+            /* String conversion. */
+            const char *s = va_arg (args, char *);
+            if (s == NULL)
+              s = "(null)";
+
+            /* Limit string length according to precision.
+               Note: if c.precision == -1 then strnlen() will get
+               SIZE_MAX for MAXLEN, which is just what we want. */
+            format_string (s, strnlen (s, c.precision), &c, output, aux);
+          }
+          break;
+          
+        case 'p':
+          {
+            /* Pointer conversion.
+               Format pointers as %#x. */
+            void *p = va_arg (args, void *);
+
+            c.flags = POUND;
+            format_integer ((uintptr_t) p, false, false,
+                            &base_x, &c, output, aux);
+          }
+          break;
+      
+        case 'f':
+        case 'e':
+        case 'E':
+        case 'g':
+        case 'G':
+        case 'n':
+          /* We don't support floating-point arithmetic,
+             and %n can be part of a security hole. */
+          __printf ("<<no %%%c in kernel>>", output, aux, *format);
+          break;
+
+        default:
+          __printf ("<<no %%%c conversion>>", output, aux, *format);
+          break;
+        }
+    }
+}
+
+/* Parses conversion option characters starting at FORMAT and
+   initializes C appropriately.  Returns the character in FORMAT
+   that indicates the conversion (e.g. the `d' in `%d').  Uses
+   *ARGS for `*' field widths and precisions. */
+static const char *
+parse_conversion (const char *format, struct printf_conversion *c,
+                  va_list *args) 
+{
+  /* Parse flag characters. */
+  c->flags = 0;
+  for (;;) 
+    {
+      switch (*format++) 
+        {
+        case '-':
+          c->flags |= MINUS;
+          break;
+        case '+':
+          c->flags |= PLUS;
+          break;
+        case ' ':
+          c->flags |= SPACE;
+          break;
+        case '#':
+          c->flags |= POUND;
+          break;
+        case '0':
+          c->flags |= ZERO;
+          break;
+        case '\'':
+          c->flags |= GROUP;
+          break;
+        default:
+          format--;
+          goto not_a_flag;
+        }
+    }
+ not_a_flag:
+  if (c->flags & MINUS)
+    c->flags &= ~ZERO;
+  if (c->flags & PLUS)
+    c->flags &= ~SPACE;
+
+  /* Parse field width. */
+  c->width = 0;
+  if (*format == '*')
+    {
+      format++;
+      c->width = va_arg (*args, int);
+    }
+  else 
+    {
+      for (; isdigit (*format); format++)
+        c->width = c->width * 10 + *format - '0';
+    }
+  if (c->width < 0) 
+    {
+      c->width = -c->width;
+      c->flags |= MINUS;
+    }
+      
+  /* Parse precision. */
+  c->precision = -1;
+  if (*format == '.') 
+    {
+      format++;
+      if (*format == '*') 
+        {
+          format++;
+          c->precision = va_arg (*args, int);
+        }
+      else 
+        {
+          c->precision = 0;
+          for (; isdigit (*format); format++)
+            c->precision = c->precision * 10 + *format - '0';
+        }
+      if (c->precision < 0) 
+        c->precision = -1;
+    }
+  if (c->precision >= 0)
+    c->flags &= ~ZERO;
+
+  /* Parse type. */
+  c->type = INT;
+  switch (*format++) 
+    {
+    case 'h':
+      if (*format == 'h') 
+        {
+          format++;
+          c->type = CHAR;
+        }
+      else
+        c->type = SHORT;
+      break;
+      
+    case 'j':
+      c->type = INTMAX;
+      break;
+
+    case 'l':
+      if (*format == 'l')
+        {
+          format++;
+          c->type = LONGLONG;
+        }
+      else
+        c->type = LONG;
+      break;
+
+    case 't':
+      c->type = PTRDIFFT;
+      break;
+
+    case 'z':
+      c->type = SIZET;
+      break;
+
+    default:
+      format--;
+      break;
+    }
+
+  return format;
+}
+
+/* Performs an integer conversion, writing output to OUTPUT with
+   auxiliary data AUX.  The integer converted has absolute value
+   VALUE.  If IS_SIGNED is true, does a signed conversion with
+   NEGATIVE indicating a negative value; otherwise does an
+   unsigned conversion and ignores NEGATIVE.  The output is done
+   according to the provided base B.  Details of the conversion
+   are in C. */
+static void
+format_integer (uintmax_t value, bool is_signed, bool negative, 
+                const struct integer_base *b,
+                const struct printf_conversion *c,
+                void (*output) (char, void *), void *aux)
+{
+  char buf[64], *cp;            /* Buffer and current position. */
+  int x;                        /* `x' character to use or 0 if none. */
+  int sign;                     /* Sign character or 0 if none. */
+  int precision;                /* Rendered precision. */
+  int pad_cnt;                  /* # of pad characters to fill field width. */
+  int digit_cnt;                /* # of digits output so far. */
+
+  /* Determine sign character, if any.
+     An unsigned conversion will never have a sign character,
+     even if one of the flags requests one. */
+  sign = 0;
+  if (is_signed) 
+    {
+      if (c->flags & PLUS)
+        sign = negative ? '-' : '+';
+      else if (c->flags & SPACE)
+        sign = negative ? '-' : ' ';
+      else if (negative)
+        sign = '-';
+    }
+
+  /* Determine whether to include `0x' or `0X'.
+     It will only be included with a hexadecimal conversion of a
+     nonzero value with the # flag. */
+  x = (c->flags & POUND) && value ? b->x : 0;
+
+  /* Accumulate digits into buffer.
+     This algorithm produces digits in reverse order, so later we
+     will output the buffer's content in reverse. */
+  cp = buf;
+  digit_cnt = 0;
+  while (value > 0) 
+    {
+      if ((c->flags & GROUP) && digit_cnt > 0 && digit_cnt % b->group == 0)
+        *cp++ = ',';
+      *cp++ = b->digits[value % b->base];
+      value /= b->base;
+      digit_cnt++;
+    }
+
+  /* Append enough zeros to match precision.
+     If requested precision is 0, then a value of zero is
+     rendered as a null string, otherwise as "0".
+     If the # flag is used with base 8, the result must always
+     begin with a zero. */
+  precision = c->precision < 0 ? 1 : c->precision;
+  while (cp - buf < precision && cp < buf + sizeof buf - 1)
+    *cp++ = '0';
+  if ((c->flags & POUND) && b->base == 8 && (cp == buf || cp[-1] != '0'))
+    *cp++ = '0';
+
+  /* Calculate number of pad characters to fill field width. */
+  pad_cnt = c->width - (cp - buf) - (x ? 2 : 0) - (sign != 0);
+  if (pad_cnt < 0)
+    pad_cnt = 0;
+
+  /* Do output. */
+  if ((c->flags & (MINUS | ZERO)) == 0)
+    output_dup (' ', pad_cnt, output, aux);
+  if (sign)
+    output (sign, aux);
+  if (x) 
+    {
+      output ('0', aux);
+      output (x, aux); 
+    }
+  if (c->flags & ZERO)
+    output_dup ('0', pad_cnt, output, aux);
+  while (cp > buf)
+    output (*--cp, aux);
+  if (c->flags & MINUS)
+    output_dup (' ', pad_cnt, output, aux);
+}
+
+/* Writes CH to OUTPUT with auxiliary data AUX, CNT times. */
+static void
+output_dup (char ch, size_t cnt, void (*output) (char, void *), void *aux) 
+{
+  while (cnt-- > 0)
+    output (ch, aux);
+}
+
+/* Formats the LENGTH characters starting at STRING according to
+   the conversion specified in C.  Writes output to OUTPUT with
+   auxiliary data AUX. */
+static void
+format_string (const char *string, int length,
+               struct printf_conversion *c,
+               void (*output) (char, void *), void *aux) 
+{
+  int i;
+  if (c->width > length && (c->flags & MINUS) == 0)
+    output_dup (' ', c->width - length, output, aux);
+  for (i = 0; i < length; i++)
+    output (string[i], aux);
+  if (c->width > length && (c->flags & MINUS) != 0)
+    output_dup (' ', c->width - length, output, aux);
+}
+
+/* Wrapper for __vprintf() that converts varargs into a
+   va_list. */
+void
+__printf (const char *format,
+          void (*output) (char, void *), void *aux, ...) 
+{
+  va_list args;
+
+  va_start (args, aux);
+  __vprintf (format, args, output, aux);
+  va_end (args);
+}
+
+/* Dumps the SIZE bytes in BUF to the console as hex bytes
+   arranged 16 per line.  Numeric offsets are also included,
+   starting at OFS for the first byte in BUF.  If ASCII is true
+   then the corresponding ASCII characters are also rendered
+   alongside. */   
+void
+hex_dump (uintptr_t ofs, const void *buf_, size_t size, bool ascii)
+{
+  const uint8_t *buf = buf_;
+  const size_t per_line = 16; /* Maximum bytes per line. */
+
+  while (size > 0)
+    {
+      size_t start, end, n;
+      size_t i;
+      
+      /* Number of bytes on this line. */
+      start = ofs % per_line;
+      end = per_line;
+      if (end - start > size)
+        end = start + size;
+      n = end - start;
+
+      /* Print line. */
+      printf ("%08jx  ", (uintmax_t) ROUND_DOWN (ofs, per_line));
+      for (i = 0; i < start; i++)
+        printf ("   ");
+      for (; i < end; i++) 
+        printf ("%02hhx%c",
+                buf[i - start], i == per_line / 2 - 1? '-' : ' ');
+      if (ascii) 
+        {
+          for (; i < per_line; i++)
+            printf ("   ");
+          printf ("|");
+          for (i = 0; i < start; i++)
+            printf (" ");
+          for (; i < end; i++)
+            printf ("%c",
+                    isprint (buf[i - start]) ? buf[i - start] : '.');
+          for (; i < per_line; i++)
+            printf (" ");
+          printf ("|");
+        }
+      printf ("\n");
+
+      ofs += n;
+      buf += n;
+      size -= n;
+    }
+}
+
+/* Prints SIZE, which represents a number of bytes, in a
+   human-readable format, e.g. "256 kB". */
+void
+print_human_readable_size (uint64_t size) 
+{
+  if (size == 1)
+    printf ("1 byte");
+  else 
+    {
+      static const char *factors[] = {"bytes", "kB", "MB", "GB", "TB", NULL};
+      const char **fp;
+
+      for (fp = factors; size >= 1024 && fp[1] != NULL; fp++)
+        size /= 1024;
+      printf ("%"PRIu64" %s", size, *fp);
+    }
+}
diff --git a/src/lib/stdio.h b/src/lib/stdio.h
new file mode 100644
index 0000000..2739c0a
--- /dev/null
+++ b/src/lib/stdio.h
@@ -0,0 +1,40 @@
+#ifndef __LIB_STDIO_H
+#define __LIB_STDIO_H
+
+#include <debug.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/* Include lib/user/stdio.h or lib/kernel/stdio.h, as
+   appropriate. */
+#include_next <stdio.h>
+
+/* Predefined file handles. */
+#define STDIN_FILENO 0
+#define STDOUT_FILENO 1
+
+/* Standard functions. */
+int printf (const char *, ...) PRINTF_FORMAT (1, 2);
+int snprintf (char *, size_t, const char *, ...) PRINTF_FORMAT (3, 4);
+int vprintf (const char *, va_list) PRINTF_FORMAT (1, 0);
+int vsnprintf (char *, size_t, const char *, va_list) PRINTF_FORMAT (3, 0);
+int putchar (int);
+int puts (const char *);
+
+/* Nonstandard functions. */
+void hex_dump (uintptr_t ofs, const void *, size_t size, bool ascii);
+void print_human_readable_size (uint64_t sz);
+
+/* Internal functions. */
+void __vprintf (const char *format, va_list args,
+                void (*output) (char, void *), void *aux);
+void __printf (const char *format,
+               void (*output) (char, void *), void *aux, ...);
+
+/* Try to be helpful. */
+#define sprintf dont_use_sprintf_use_snprintf
+#define vsprintf dont_use_vsprintf_use_vsnprintf
+
+#endif /* lib/stdio.h */
diff --git a/src/lib/stdlib.c b/src/lib/stdlib.c
new file mode 100644
index 0000000..84c7f61
--- /dev/null
+++ b/src/lib/stdlib.c
@@ -0,0 +1,208 @@
+#include <ctype.h>
+#include <debug.h>
+#include <random.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+/* Converts a string representation of a signed decimal integer
+   in S into an `int', which is returned. */
+int
+atoi (const char *s) 
+{
+  bool negative;
+  int value;
+
+  ASSERT (s != NULL);
+
+  /* Skip white space. */
+  while (isspace ((unsigned char) *s))
+    s++;
+
+  /* Parse sign. */
+  negative = false;
+  if (*s == '+')
+    s++;
+  else if (*s == '-')
+    {
+      negative = true;
+      s++;
+    }
+
+  /* Parse digits.  We always initially parse the value as
+     negative, and then make it positive later, because the
+     negative range of an int is bigger than the positive range
+     on a 2's complement system. */
+  for (value = 0; isdigit (*s); s++)
+    value = value * 10 - (*s - '0');
+  if (!negative)
+    value = -value;
+
+  return value;
+}
+
+/* Compares A and B by calling the AUX function. */
+static int
+compare_thunk (const void *a, const void *b, void *aux) 
+{
+  int (**compare) (const void *, const void *) = aux;
+  return (*compare) (a, b);
+}
+
+/* Sorts ARRAY, which contains CNT elements of SIZE bytes each,
+   using COMPARE.  When COMPARE is passed a pair of elements A
+   and B, respectively, it must return a strcmp()-type result,
+   i.e. less than zero if A < B, zero if A == B, greater than
+   zero if A > B.  Runs in O(n lg n) time and O(1) space in
+   CNT. */
+void
+qsort (void *array, size_t cnt, size_t size,
+       int (*compare) (const void *, const void *)) 
+{
+  sort (array, cnt, size, compare_thunk, &compare);
+}
+
+/* Swaps elements with 1-based indexes A_IDX and B_IDX in ARRAY
+   with elements of SIZE bytes each. */
+static void
+do_swap (unsigned char *array, size_t a_idx, size_t b_idx, size_t size)
+{
+  unsigned char *a = array + (a_idx - 1) * size;
+  unsigned char *b = array + (b_idx - 1) * size;
+  size_t i;
+
+  for (i = 0; i < size; i++)
+    {
+      unsigned char t = a[i];
+      a[i] = b[i];
+      b[i] = t;
+    }
+}
+
+/* Compares elements with 1-based indexes A_IDX and B_IDX in
+   ARRAY with elements of SIZE bytes each, using COMPARE to
+   compare elements, passing AUX as auxiliary data, and returns a
+   strcmp()-type result. */
+static int
+do_compare (unsigned char *array, size_t a_idx, size_t b_idx, size_t size,
+            int (*compare) (const void *, const void *, void *aux),
+            void *aux) 
+{
+  return compare (array + (a_idx - 1) * size, array + (b_idx - 1) * size, aux);
+}
+
+/* "Float down" the element with 1-based index I in ARRAY of CNT
+   elements of SIZE bytes each, using COMPARE to compare
+   elements, passing AUX as auxiliary data. */
+static void
+heapify (unsigned char *array, size_t i, size_t cnt, size_t size,
+         int (*compare) (const void *, const void *, void *aux),
+         void *aux) 
+{
+  for (;;) 
+    {
+      /* Set `max' to the index of the largest element among I
+         and its children (if any). */
+      size_t left = 2 * i;
+      size_t right = 2 * i + 1;
+      size_t max = i;
+      if (left <= cnt && do_compare (array, left, max, size, compare, aux) > 0)
+        max = left;
+      if (right <= cnt
+          && do_compare (array, right, max, size, compare, aux) > 0) 
+        max = right;
+
+      /* If the maximum value is already in element I, we're
+         done. */
+      if (max == i)
+        break;
+
+      /* Swap and continue down the heap. */
+      do_swap (array, i, max, size);
+      i = max;
+    }
+}
+
+/* Sorts ARRAY, which contains CNT elements of SIZE bytes each,
+   using COMPARE to compare elements, passing AUX as auxiliary
+   data.  When COMPARE is passed a pair of elements A and B,
+   respectively, it must return a strcmp()-type result, i.e. less
+   than zero if A < B, zero if A == B, greater than zero if A >
+   B.  Runs in O(n lg n) time and O(1) space in CNT. */
+void
+sort (void *array, size_t cnt, size_t size,
+      int (*compare) (const void *, const void *, void *aux),
+      void *aux) 
+{
+  size_t i;
+
+  ASSERT (array != NULL || cnt == 0);
+  ASSERT (compare != NULL);
+  ASSERT (size > 0);
+
+  /* Build a heap. */
+  for (i = cnt / 2; i > 0; i--)
+    heapify (array, i, cnt, size, compare, aux);
+
+  /* Sort the heap. */
+  for (i = cnt; i > 1; i--) 
+    {
+      do_swap (array, 1, i, size);
+      heapify (array, 1, i - 1, size, compare, aux); 
+    }
+}
+
+/* Searches ARRAY, which contains CNT elements of SIZE bytes
+   each, for the given KEY.  Returns a match is found, otherwise
+   a null pointer.  If there are multiple matches, returns an
+   arbitrary one of them.
+
+   ARRAY must be sorted in order according to COMPARE.
+
+   Uses COMPARE to compare elements.  When COMPARE is passed a
+   pair of elements A and B, respectively, it must return a
+   strcmp()-type result, i.e. less than zero if A < B, zero if A
+   == B, greater than zero if A > B. */
+void *
+bsearch (const void *key, const void *array, size_t cnt,
+         size_t size, int (*compare) (const void *, const void *)) 
+{
+  return binary_search (key, array, cnt, size, compare_thunk, &compare);
+}
+
+/* Searches ARRAY, which contains CNT elements of SIZE bytes
+   each, for the given KEY.  Returns a match is found, otherwise
+   a null pointer.  If there are multiple matches, returns an
+   arbitrary one of them.
+
+   ARRAY must be sorted in order according to COMPARE.
+
+   Uses COMPARE to compare elements, passing AUX as auxiliary
+   data.  When COMPARE is passed a pair of elements A and B,
+   respectively, it must return a strcmp()-type result, i.e. less
+   than zero if A < B, zero if A == B, greater than zero if A >
+   B. */
+void *
+binary_search (const void *key, const void *array, size_t cnt, size_t size,
+               int (*compare) (const void *, const void *, void *aux),
+               void *aux) 
+{
+  const unsigned char *first = array;
+  const unsigned char *last = array + size * cnt;
+
+  while (first < last) 
+    {
+      size_t range = (last - first) / size;
+      const unsigned char *middle = first + (range / 2) * size;
+      int cmp = compare (key, middle, aux);
+
+      if (cmp < 0) 
+        last = middle;
+      else if (cmp > 0) 
+        first = middle + size;
+      else
+        return (void *) middle;
+    }
+  
+  return NULL;
+}
+
diff --git a/src/lib/stdlib.h b/src/lib/stdlib.h
new file mode 100644
index 0000000..d14afa3
--- /dev/null
+++ b/src/lib/stdlib.h
@@ -0,0 +1,22 @@
+#ifndef __LIB_STDLIB_H
+#define __LIB_STDLIB_H
+
+#include <stddef.h>
+
+/* Standard functions. */
+int atoi (const char *);
+void qsort (void *array, size_t cnt, size_t size,
+            int (*compare) (const void *, const void *));
+void *bsearch (const void *key, const void *array, size_t cnt,
+               size_t size, int (*compare) (const void *, const void *));
+
+/* Nonstandard functions. */
+void sort (void *array, size_t cnt, size_t size,
+           int (*compare) (const void *, const void *, void *aux),
+           void *aux);
+void *binary_search (const void *key, const void *array, size_t cnt,
+                     size_t size,
+                     int (*compare) (const void *, const void *, void *aux),
+                     void *aux);
+
+#endif /* lib/stdlib.h */
diff --git a/src/lib/string.c b/src/lib/string.c
new file mode 100644
index 0000000..d223c89
--- /dev/null
+++ b/src/lib/string.c
@@ -0,0 +1,375 @@
+#include <string.h>
+#include <debug.h>
+
+/* Copies SIZE bytes from SRC to DST, which must not overlap.
+   Returns DST. */
+void *
+memcpy (void *dst_, const void *src_, size_t size) 
+{
+  unsigned char *dst = dst_;
+  const unsigned char *src = src_;
+
+  ASSERT (dst != NULL || size == 0);
+  ASSERT (src != NULL || size == 0);
+
+  while (size-- > 0)
+    *dst++ = *src++;
+
+  return dst_;
+}
+
+/* Copies SIZE bytes from SRC to DST, which are allowed to
+   overlap.  Returns DST. */
+void *
+memmove (void *dst_, const void *src_, size_t size) 
+{
+  unsigned char *dst = dst_;
+  const unsigned char *src = src_;
+
+  ASSERT (dst != NULL || size == 0);
+  ASSERT (src != NULL || size == 0);
+
+  if (dst < src) 
+    {
+      while (size-- > 0)
+        *dst++ = *src++;
+    }
+  else 
+    {
+      dst += size;
+      src += size;
+      while (size-- > 0)
+        *--dst = *--src;
+    }
+
+  return dst;
+}
+
+/* Find the first differing byte in the two blocks of SIZE bytes
+   at A and B.  Returns a positive value if the byte in A is
+   greater, a negative value if the byte in B is greater, or zero
+   if blocks A and B are equal. */
+int
+memcmp (const void *a_, const void *b_, size_t size) 
+{
+  const unsigned char *a = a_;
+  const unsigned char *b = b_;
+
+  ASSERT (a != NULL || size == 0);
+  ASSERT (b != NULL || size == 0);
+
+  for (; size-- > 0; a++, b++)
+    if (*a != *b)
+      return *a > *b ? +1 : -1;
+  return 0;
+}
+
+/* Finds the first differing characters in strings A and B.
+   Returns a positive value if the character in A (as an unsigned
+   char) is greater, a negative value if the character in B (as
+   an unsigned char) is greater, or zero if strings A and B are
+   equal. */
+int
+strcmp (const char *a_, const char *b_) 
+{
+  const unsigned char *a = (const unsigned char *) a_;
+  const unsigned char *b = (const unsigned char *) b_;
+
+  ASSERT (a != NULL);
+  ASSERT (b != NULL);
+
+  while (*a != '\0' && *a == *b) 
+    {
+      a++;
+      b++;
+    }
+
+  return *a < *b ? -1 : *a > *b;
+}
+
+/* Returns a pointer to the first occurrence of CH in the first
+   SIZE bytes starting at BLOCK.  Returns a null pointer if CH
+   does not occur in BLOCK. */
+void *
+memchr (const void *block_, int ch_, size_t size) 
+{
+  const unsigned char *block = block_;
+  unsigned char ch = ch_;
+
+  ASSERT (block != NULL || size == 0);
+
+  for (; size-- > 0; block++)
+    if (*block == ch)
+      return (void *) block;
+
+  return NULL;
+}
+
+/* Finds and returns the first occurrence of C in STRING, or a
+   null pointer if C does not appear in STRING.  If C == '\0'
+   then returns a pointer to the null terminator at the end of
+   STRING. */
+char *
+strchr (const char *string, int c_) 
+{
+  char c = c_;
+
+  ASSERT (string != NULL);
+
+  for (;;) 
+    if (*string == c)
+      return (char *) string;
+    else if (*string == '\0')
+      return NULL;
+    else
+      string++;
+}
+
+/* Returns the length of the initial substring of STRING that
+   consists of characters that are not in STOP. */
+size_t
+strcspn (const char *string, const char *stop) 
+{
+  size_t length;
+
+  for (length = 0; string[length] != '\0'; length++)
+    if (strchr (stop, string[length]) != NULL)
+      break;
+  return length;
+}
+
+/* Returns a pointer to the first character in STRING that is
+   also in STOP.  If no character in STRING is in STOP, returns a
+   null pointer. */
+char *
+strpbrk (const char *string, const char *stop) 
+{
+  for (; *string != '\0'; string++)
+    if (strchr (stop, *string) != NULL)
+      return (char *) string;
+  return NULL;
+}
+
+/* Returns a pointer to the last occurrence of C in STRING.
+   Returns a null pointer if C does not occur in STRING. */
+char *
+strrchr (const char *string, int c_) 
+{
+  char c = c_;
+  const char *p = NULL;
+
+  for (; *string != '\0'; string++)
+    if (*string == c)
+      p = string;
+  return (char *) p;
+}
+
+/* Returns the length of the initial substring of STRING that
+   consists of characters in SKIP. */
+size_t
+strspn (const char *string, const char *skip) 
+{
+  size_t length;
+  
+  for (length = 0; string[length] != '\0'; length++)
+    if (strchr (skip, string[length]) == NULL)
+      break;
+  return length;
+}
+
+/* Returns a pointer to the first occurrence of NEEDLE within
+   HAYSTACK.  Returns a null pointer if NEEDLE does not exist
+   within HAYSTACK. */
+char *
+strstr (const char *haystack, const char *needle) 
+{
+  size_t haystack_len = strlen (haystack);
+  size_t needle_len = strlen (needle);
+
+  if (haystack_len >= needle_len) 
+    {
+      size_t i;
+
+      for (i = 0; i <= haystack_len - needle_len; i++)
+        if (!memcmp (haystack + i, needle, needle_len))
+          return (char *) haystack + i;
+    }
+
+  return NULL;
+}
+
+/* Breaks a string into tokens separated by DELIMITERS.  The
+   first time this function is called, S should be the string to
+   tokenize, and in subsequent calls it must be a null pointer.
+   SAVE_PTR is the address of a `char *' variable used to keep
+   track of the tokenizer's position.  The return value each time
+   is the next token in the string, or a null pointer if no
+   tokens remain.
+
+   This function treats multiple adjacent delimiters as a single
+   delimiter.  The returned tokens will never be length 0.
+   DELIMITERS may change from one call to the next within a
+   single string.
+
+   strtok_r() modifies the string S, changing delimiters to null
+   bytes.  Thus, S must be a modifiable string.  String literals,
+   in particular, are *not* modifiable in C, even though for
+   backward compatibility they are not `const'.
+
+   Example usage:
+
+   char s[] = "  String to  tokenize. ";
+   char *token, *save_ptr;
+
+   for (token = strtok_r (s, " ", &save_ptr); token != NULL;
+        token = strtok_r (NULL, " ", &save_ptr))
+     printf ("'%s'\n", token);
+
+   outputs:
+
+     'String'
+     'to'
+     'tokenize.'
+*/
+char *
+strtok_r (char *s, const char *delimiters, char **save_ptr) 
+{
+  char *token;
+  
+  ASSERT (delimiters != NULL);
+  ASSERT (save_ptr != NULL);
+
+  /* If S is nonnull, start from it.
+     If S is null, start from saved position. */
+  if (s == NULL)
+    s = *save_ptr;
+  ASSERT (s != NULL);
+
+  /* Skip any DELIMITERS at our current position. */
+  while (strchr (delimiters, *s) != NULL) 
+    {
+      /* strchr() will always return nonnull if we're searching
+         for a null byte, because every string contains a null
+         byte (at the end). */
+      if (*s == '\0')
+        {
+          *save_ptr = s;
+          return NULL;
+        }
+
+      s++;
+    }
+
+  /* Skip any non-DELIMITERS up to the end of the string. */
+  token = s;
+  while (strchr (delimiters, *s) == NULL)
+    s++;
+  if (*s != '\0') 
+    {
+      *s = '\0';
+      *save_ptr = s + 1;
+    }
+  else 
+    *save_ptr = s;
+  return token;
+}
+
+/* Sets the SIZE bytes in DST to VALUE. */
+void *
+memset (void *dst_, int value, size_t size) 
+{
+  unsigned char *dst = dst_;
+
+  ASSERT (dst != NULL || size == 0);
+  
+  while (size-- > 0)
+    *dst++ = value;
+
+  return dst_;
+}
+
+/* Returns the length of STRING. */
+size_t
+strlen (const char *string) 
+{
+  const char *p;
+
+  ASSERT (string != NULL);
+
+  for (p = string; *p != '\0'; p++)
+    continue;
+  return p - string;
+}
+
+/* If STRING is less than MAXLEN characters in length, returns
+   its actual length.  Otherwise, returns MAXLEN. */
+size_t
+strnlen (const char *string, size_t maxlen) 
+{
+  size_t length;
+
+  for (length = 0; string[length] != '\0' && length < maxlen; length++)
+    continue;
+  return length;
+}
+
+/* Copies string SRC to DST.  If SRC is longer than SIZE - 1
+   characters, only SIZE - 1 characters are copied.  A null
+   terminator is always written to DST, unless SIZE is 0.
+   Returns the length of SRC, not including the null terminator.
+
+   strlcpy() is not in the standard C library, but it is an
+   increasingly popular extension.  See
+   http://www.courtesan.com/todd/papers/strlcpy.html for
+   information on strlcpy(). */
+size_t
+strlcpy (char *dst, const char *src, size_t size) 
+{
+  size_t src_len;
+
+  ASSERT (dst != NULL);
+  ASSERT (src != NULL);
+
+  src_len = strlen (src);
+  if (size > 0) 
+    {
+      size_t dst_len = size - 1;
+      if (src_len < dst_len)
+        dst_len = src_len;
+      memcpy (dst, src, dst_len);
+      dst[dst_len] = '\0';
+    }
+  return src_len;
+}
+
+/* Concatenates string SRC to DST.  The concatenated string is
+   limited to SIZE - 1 characters.  A null terminator is always
+   written to DST, unless SIZE is 0.  Returns the length that the
+   concatenated string would have assuming that there was
+   sufficient space, not including a null terminator.
+
+   strlcat() is not in the standard C library, but it is an
+   increasingly popular extension.  See
+   http://www.courtesan.com/todd/papers/strlcpy.html for
+   information on strlcpy(). */
+size_t
+strlcat (char *dst, const char *src, size_t size) 
+{
+  size_t src_len, dst_len;
+
+  ASSERT (dst != NULL);
+  ASSERT (src != NULL);
+
+  src_len = strlen (src);
+  dst_len = strlen (dst);
+  if (size > 0 && dst_len < size) 
+    {
+      size_t copy_cnt = size - dst_len - 1;
+      if (src_len < copy_cnt)
+        copy_cnt = src_len;
+      memcpy (dst + dst_len, src, copy_cnt);
+      dst[dst_len + copy_cnt] = '\0';
+    }
+  return src_len + dst_len;
+}
+
diff --git a/src/lib/string.h b/src/lib/string.h
new file mode 100644
index 0000000..1fff82a
--- /dev/null
+++ b/src/lib/string.h
@@ -0,0 +1,35 @@
+#ifndef __LIB_STRING_H
+#define __LIB_STRING_H
+
+#include <stddef.h>
+
+/* Standard. */
+void *memcpy (void *, const void *, size_t);
+void *memmove (void *, const void *, size_t);
+char *strncat (char *, const char *, size_t);
+int memcmp (const void *, const void *, size_t);
+int strcmp (const char *, const char *);
+void *memchr (const void *, int, size_t);
+char *strchr (const char *, int);
+size_t strcspn (const char *, const char *);
+char *strpbrk (const char *, const char *);
+char *strrchr (const char *, int);
+size_t strspn (const char *, const char *);
+char *strstr (const char *, const char *);
+void *memset (void *, int, size_t);
+size_t strlen (const char *);
+
+/* Extensions. */
+size_t strlcpy (char *, const char *, size_t);
+size_t strlcat (char *, const char *, size_t);
+char *strtok_r (char *, const char *, char **);
+size_t strnlen (const char *, size_t);
+
+/* Try to be helpful. */
+#define strcpy dont_use_strcpy_use_strlcpy
+#define strncpy dont_use_strncpy_use_strlcpy
+#define strcat dont_use_strcat_use_strlcat
+#define strncat dont_use_strncat_use_strlcat
+#define strtok dont_use_strtok_use_strtok_r
+
+#endif /* lib/string.h */
diff --git a/src/lib/syscall-nr.h b/src/lib/syscall-nr.h
new file mode 100644
index 0000000..21a7af9
--- /dev/null
+++ b/src/lib/syscall-nr.h
@@ -0,0 +1,34 @@
+#ifndef __LIB_SYSCALL_NR_H
+#define __LIB_SYSCALL_NR_H
+
+/* System call numbers. */
+enum 
+  {
+    /* Projects 2 and later. */
+    SYS_HALT,                   /* Halt the operating system. */
+    SYS_EXIT,                   /* Terminate this process. */
+    SYS_EXEC,                   /* Start another process. */
+    SYS_WAIT,                   /* Wait for a child process to die. */
+    SYS_CREATE,                 /* Create a file. */
+    SYS_REMOVE,                 /* Delete a file. */
+    SYS_OPEN,                   /* Open a file. */
+    SYS_FILESIZE,               /* Obtain a file's size. */
+    SYS_READ,                   /* Read from a file. */
+    SYS_WRITE,                  /* Write to a file. */
+    SYS_SEEK,                   /* Change position in a file. */
+    SYS_TELL,                   /* Report current position in a file. */
+    SYS_CLOSE,                  /* Close a file. */
+
+    /* Project 3 and optionally project 4. */
+    SYS_MMAP,                   /* Map a file into memory. */
+    SYS_MUNMAP,                 /* Remove a memory mapping. */
+
+    /* Project 4 only. */
+    SYS_CHDIR,                  /* Change the current directory. */
+    SYS_MKDIR,                  /* Create a directory. */
+    SYS_READDIR,                /* Reads a directory entry. */
+    SYS_ISDIR,                  /* Tests if a fd represents a directory. */
+    SYS_INUMBER                 /* Returns the inode number for a fd. */
+  };
+
+#endif /* lib/syscall-nr.h */
diff --git a/src/lib/user/console.c b/src/lib/user/console.c
new file mode 100644
index 0000000..22bdc8c
--- /dev/null
+++ b/src/lib/user/console.c
@@ -0,0 +1,94 @@
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+#include <syscall-nr.h>
+
+/* The standard vprintf() function,
+   which is like printf() but uses a va_list. */
+int
+vprintf (const char *format, va_list args) 
+{
+  return vhprintf (STDOUT_FILENO, format, args);
+}
+
+/* Like printf(), but writes output to the given HANDLE. */
+int
+hprintf (int handle, const char *format, ...) 
+{
+  va_list args;
+  int retval;
+
+  va_start (args, format);
+  retval = vhprintf (handle, format, args);
+  va_end (args);
+
+  return retval;
+}
+
+/* Writes string S to the console, followed by a new-line
+   character. */
+int
+puts (const char *s) 
+{
+  write (STDOUT_FILENO, s, strlen (s));
+  putchar ('\n');
+
+  return 0;
+}
+
+/* Writes C to the console. */
+int
+putchar (int c) 
+{
+  char c2 = c;
+  write (STDOUT_FILENO, &c2, 1);
+  return c;
+}
+
+/* Auxiliary data for vhprintf_helper(). */
+struct vhprintf_aux 
+  {
+    char buf[64];       /* Character buffer. */
+    char *p;            /* Current position in buffer. */
+    int char_cnt;       /* Total characters written so far. */
+    int handle;         /* Output file handle. */
+  };
+
+static void add_char (char, void *);
+static void flush (struct vhprintf_aux *);
+
+/* Formats the printf() format specification FORMAT with
+   arguments given in ARGS and writes the output to the given
+   HANDLE. */
+int
+vhprintf (int handle, const char *format, va_list args) 
+{
+  struct vhprintf_aux aux;
+  aux.p = aux.buf;
+  aux.char_cnt = 0;
+  aux.handle = handle;
+  __vprintf (format, args, add_char, &aux);
+  flush (&aux);
+  return aux.char_cnt;
+}
+
+/* Adds C to the buffer in AUX, flushing it if the buffer fills
+   up. */
+static void
+add_char (char c, void *aux_) 
+{
+  struct vhprintf_aux *aux = aux_;
+  *aux->p++ = c;
+  if (aux->p >= aux->buf + sizeof aux->buf)
+    flush (aux);
+  aux->char_cnt++;
+}
+
+/* Flushes the buffer in AUX. */
+static void
+flush (struct vhprintf_aux *aux)
+{
+  if (aux->p > aux->buf)
+    write (aux->handle, aux->buf, aux->p - aux->buf);
+  aux->p = aux->buf;
+}
diff --git a/src/lib/user/debug.c b/src/lib/user/debug.c
new file mode 100644
index 0000000..f49b874
--- /dev/null
+++ b/src/lib/user/debug.c
@@ -0,0 +1,25 @@
+#include <debug.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <syscall.h>
+
+/* Aborts the user program, printing the source file name, line
+   number, and function name, plus a user-specific message. */
+void
+debug_panic (const char *file, int line, const char *function,
+             const char *message, ...)
+{
+  va_list args;
+
+  printf ("User process ABORT at %s:%d in %s(): ", file, line, function);
+
+  va_start (args, message);
+  vprintf (message, args);
+  printf ("\n");
+  va_end (args);
+
+  debug_backtrace ();
+  
+  exit (1);
+}
diff --git a/src/lib/user/entry.c b/src/lib/user/entry.c
new file mode 100644
index 0000000..a707c70
--- /dev/null
+++ b/src/lib/user/entry.c
@@ -0,0 +1,10 @@
+#include <syscall.h>
+
+int main (int, char *[]);
+void _start (int argc, char *argv[]);
+
+void
+_start (int argc, char *argv[]) 
+{
+  exit (main (argc, argv));
+}
diff --git a/src/lib/user/stdio.h b/src/lib/user/stdio.h
new file mode 100644
index 0000000..b9f3cc6
--- /dev/null
+++ b/src/lib/user/stdio.h
@@ -0,0 +1,7 @@
+#ifndef __LIB_USER_STDIO_H
+#define __LIB_USER_STDIO_H
+
+int hprintf (int, const char *, ...) PRINTF_FORMAT (2, 3);
+int vhprintf (int, const char *, va_list) PRINTF_FORMAT (2, 0);
+
+#endif /* lib/user/stdio.h */
diff --git a/src/lib/user/syscall.c b/src/lib/user/syscall.c
new file mode 100644
index 0000000..c8385bc
--- /dev/null
+++ b/src/lib/user/syscall.c
@@ -0,0 +1,184 @@
+#include <syscall.h>
+#include "../syscall-nr.h"
+
+/* Invokes syscall NUMBER, passing no arguments, and returns the
+   return value as an `int'. */
+#define syscall0(NUMBER)                                        \
+        ({                                                      \
+          int retval;                                           \
+          asm volatile                                          \
+            ("pushl %[number]; int $0x30; addl $4, %%esp"       \
+               : "=a" (retval)                                  \
+               : [number] "i" (NUMBER)                          \
+               : "memory");                                     \
+          retval;                                               \
+        })
+
+/* Invokes syscall NUMBER, passing argument ARG0, and returns the
+   return value as an `int'. */
+#define syscall1(NUMBER, ARG0)                                           \
+        ({                                                               \
+          int retval;                                                    \
+          asm volatile                                                   \
+            ("pushl %[arg0]; pushl %[number]; int $0x30; addl $8, %%esp" \
+               : "=a" (retval)                                           \
+               : [number] "i" (NUMBER),                                  \
+                 [arg0] "g" (ARG0)                                       \
+               : "memory");                                              \
+          retval;                                                        \
+        })
+
+/* Invokes syscall NUMBER, passing arguments ARG0 and ARG1, and
+   returns the return value as an `int'. */
+#define syscall2(NUMBER, ARG0, ARG1)                            \
+        ({                                                      \
+          int retval;                                           \
+          asm volatile                                          \
+            ("pushl %[arg1]; pushl %[arg0]; "                   \
+             "pushl %[number]; int $0x30; addl $12, %%esp"      \
+               : "=a" (retval)                                  \
+               : [number] "i" (NUMBER),                         \
+                 [arg0] "r" (ARG0),                             \
+                 [arg1] "r" (ARG1)                              \
+               : "memory");                                     \
+          retval;                                               \
+        })
+
+/* Invokes syscall NUMBER, passing arguments ARG0, ARG1, and
+   ARG2, and returns the return value as an `int'. */
+#define syscall3(NUMBER, ARG0, ARG1, ARG2)                      \
+        ({                                                      \
+          int retval;                                           \
+          asm volatile                                          \
+            ("pushl %[arg2]; pushl %[arg1]; pushl %[arg0]; "    \
+             "pushl %[number]; int $0x30; addl $16, %%esp"      \
+               : "=a" (retval)                                  \
+               : [number] "i" (NUMBER),                         \
+                 [arg0] "r" (ARG0),                             \
+                 [arg1] "r" (ARG1),                             \
+                 [arg2] "r" (ARG2)                              \
+               : "memory");                                     \
+          retval;                                               \
+        })
+
+void
+halt (void) 
+{
+  syscall0 (SYS_HALT);
+  NOT_REACHED ();
+}
+
+void
+exit (int status)
+{
+  syscall1 (SYS_EXIT, status);
+  NOT_REACHED ();
+}
+
+pid_t
+exec (const char *file)
+{
+  return (pid_t) syscall1 (SYS_EXEC, file);
+}
+
+int
+wait (pid_t pid)
+{
+  return syscall1 (SYS_WAIT, pid);
+}
+
+bool
+create (const char *file, unsigned initial_size)
+{
+  return syscall2 (SYS_CREATE, file, initial_size);
+}
+
+bool
+remove (const char *file)
+{
+  return syscall1 (SYS_REMOVE, file);
+}
+
+int
+open (const char *file)
+{
+  return syscall1 (SYS_OPEN, file);
+}
+
+int
+filesize (int fd) 
+{
+  return syscall1 (SYS_FILESIZE, fd);
+}
+
+int
+read (int fd, void *buffer, unsigned size)
+{
+  return syscall3 (SYS_READ, fd, buffer, size);
+}
+
+int
+write (int fd, const void *buffer, unsigned size)
+{
+  return syscall3 (SYS_WRITE, fd, buffer, size);
+}
+
+void
+seek (int fd, unsigned position) 
+{
+  syscall2 (SYS_SEEK, fd, position);
+}
+
+unsigned
+tell (int fd) 
+{
+  return syscall1 (SYS_TELL, fd);
+}
+
+void
+close (int fd)
+{
+  syscall1 (SYS_CLOSE, fd);
+}
+
+mapid_t
+mmap (int fd, void *addr)
+{
+  return syscall2 (SYS_MMAP, fd, addr);
+}
+
+void
+munmap (mapid_t mapid)
+{
+  syscall1 (SYS_MUNMAP, mapid);
+}
+
+bool
+chdir (const char *dir)
+{
+  return syscall1 (SYS_CHDIR, dir);
+}
+
+bool
+mkdir (const char *dir)
+{
+  return syscall1 (SYS_MKDIR, dir);
+}
+
+bool
+readdir (int fd, char name[READDIR_MAX_LEN + 1]) 
+{
+  return syscall2 (SYS_READDIR, fd, name);
+}
+
+bool
+isdir (int fd) 
+{
+  return syscall1 (SYS_ISDIR, fd);
+}
+
+int
+inumber (int fd) 
+{
+  return syscall1 (SYS_INUMBER, fd);
+}
diff --git a/src/lib/user/syscall.h b/src/lib/user/syscall.h
new file mode 100644
index 0000000..8a9e0c0
--- /dev/null
+++ b/src/lib/user/syscall.h
@@ -0,0 +1,48 @@
+#ifndef __LIB_USER_SYSCALL_H
+#define __LIB_USER_SYSCALL_H
+
+#include <stdbool.h>
+#include <debug.h>
+
+/* Process identifier. */
+typedef int pid_t;
+#define PID_ERROR ((pid_t) -1)
+
+/* Map region identifier. */
+typedef int mapid_t;
+#define MAP_FAILED ((mapid_t) -1)
+
+/* Maximum characters in a filename written by readdir(). */
+#define READDIR_MAX_LEN 14
+
+/* Typical return values from main() and arguments to exit(). */
+#define EXIT_SUCCESS 0          /* Successful execution. */
+#define EXIT_FAILURE 1          /* Unsuccessful execution. */
+
+/* Projects 2 and later. */
+void halt (void) NO_RETURN;
+void exit (int status) NO_RETURN;
+pid_t exec (const char *file);
+int wait (pid_t);
+bool create (const char *file, unsigned initial_size);
+bool remove (const char *file);
+int open (const char *file);
+int filesize (int fd);
+int read (int fd, void *buffer, unsigned length);
+int write (int fd, const void *buffer, unsigned length);
+void seek (int fd, unsigned position);
+unsigned tell (int fd);
+void close (int fd);
+
+/* Project 3 and optionally project 4. */
+mapid_t mmap (int fd, void *addr);
+void munmap (mapid_t);
+
+/* Project 4 only. */
+bool chdir (const char *dir);
+bool mkdir (const char *dir);
+bool readdir (int fd, char name[READDIR_MAX_LEN + 1]);
+bool isdir (int fd);
+int inumber (int fd);
+
+#endif /* lib/user/syscall.h */
diff --git a/src/lib/user/user.lds b/src/lib/user/user.lds
new file mode 100644
index 0000000..1bf30b1
--- /dev/null
+++ b/src/lib/user/user.lds
@@ -0,0 +1,57 @@
+OUTPUT_FORMAT("elf32-i386")
+OUTPUT_ARCH(i386)
+ENTRY(_start)
+
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  __executable_start = 0x08048000 + SIZEOF_HEADERS;
+  . = 0x08048000 + SIZEOF_HEADERS;
+  .text : { *(.text) } = 0x90
+  .rodata : { *(.rodata) }
+
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  */
+  . = ALIGN (0x1000) - ((0x1000 - .) & (0x1000 - 1)); 
+  . = DATA_SEGMENT_ALIGN (0x1000, 0x1000);
+
+  .data : { *(.data) }
+  .bss : { *(.bss) *(.testEndmem) }
+
+  /* Stabs debugging sections.  */
+  .stab          0 : { *(.stab) }
+  .stabstr       0 : { *(.stabstr) }
+  .stab.excl     0 : { *(.stab.excl) }
+  .stab.exclstr  0 : { *(.stab.exclstr) }
+  .stab.index    0 : { *(.stab.index) }
+  .stab.indexstr 0 : { *(.stab.indexstr) }
+  .comment       0 : { *(.comment) }
+
+  /* DWARF debug sections.
+  Symbols in the DWARF debugging sections are relative to the beginning
+  of the section so we begin them at 0.  */
+  /* DWARF 1 */
+  .debug          0 : { *(.debug) }
+  .line           0 : { *(.line) }
+  /* GNU DWARF 1 extensions */
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  /* DWARF 1.1 and DWARF 2 */
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  /* DWARF 2 */
+  .debug_info     0 : { *(.debug_info .gnu.linkonce.wi.*) }
+  .debug_abbrev   0 : { *(.debug_abbrev) }
+  .debug_line     0 : { *(.debug_line) }
+  .debug_frame    0 : { *(.debug_frame) }
+  .debug_str      0 : { *(.debug_str) }
+  .debug_loc      0 : { *(.debug_loc) }
+  .debug_macinfo  0 : { *(.debug_macinfo) }
+  /* SGI/MIPS DWARF 2 extensions */
+  .debug_weaknames 0 : { *(.debug_weaknames) }
+  .debug_funcnames 0 : { *(.debug_funcnames) }
+  .debug_typenames 0 : { *(.debug_typenames) }
+  .debug_varnames  0 : { *(.debug_varnames) }
+  /DISCARD/ : { *(.note.GNU-stack) }
+  /DISCARD/ : { *(.eh_frame) }
+}
diff --git a/src/lib/ustar.c b/src/lib/ustar.c
new file mode 100644
index 0000000..49af69a
--- /dev/null
+++ b/src/lib/ustar.c
@@ -0,0 +1,228 @@
+#include <ustar.h>
+#include <limits.h>
+#include <packed.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Header for ustar-format tar archive.  See the documentation of
+   the "pax" utility in [SUSv3] for the the "ustar" format
+   specification. */
+struct ustar_header
+  {
+    char name[100];             /* File name.  Null-terminated if room. */
+    char mode[8];               /* Permissions as octal string. */
+    char uid[8];                /* User ID as octal string. */
+    char gid[8];                /* Group ID as octal string. */
+    char size[12];              /* File size in bytes as octal string. */
+    char mtime[12];             /* Modification time in seconds
+                                   from Jan 1, 1970, as octal string. */
+    char chksum[8];             /* Sum of octets in header as octal string. */
+    char typeflag;              /* An enum ustar_type value. */
+    char linkname[100];         /* Name of link target.
+                                   Null-terminated if room. */
+    char magic[6];              /* "ustar\0" */
+    char version[2];            /* "00" */
+    char uname[32];             /* User name, always null-terminated. */
+    char gname[32];             /* Group name, always null-terminated. */
+    char devmajor[8];           /* Device major number as octal string. */
+    char devminor[8];           /* Device minor number as octal string. */
+    char prefix[155];           /* Prefix to file name.
+                                   Null-terminated if room. */
+    char padding[12];           /* Pad to 512 bytes. */
+  }
+PACKED;
+
+/* Returns the checksum for the given ustar format HEADER. */
+static unsigned int
+calculate_chksum (const struct ustar_header *h)
+{
+  const uint8_t *header = (const uint8_t *) h;
+  unsigned int chksum;
+  size_t i;
+
+  chksum = 0;
+  for (i = 0; i < USTAR_HEADER_SIZE; i++)
+    {
+      /* The ustar checksum is calculated as if the chksum field
+         were all spaces. */
+      const size_t chksum_start = offsetof (struct ustar_header, chksum);
+      const size_t chksum_end = chksum_start + sizeof h->chksum;
+      bool in_chksum_field = i >= chksum_start && i < chksum_end;
+      chksum += in_chksum_field ? ' ' : header[i];
+    }
+  return chksum;
+}
+
+/* Drop possibly dangerous prefixes from FILE_NAME and return the
+   stripped name.  An archive with file names that start with "/"
+   or "../" could cause a naive tar extractor to write to
+   arbitrary parts of the file system, not just the destination
+   directory.  We don't want to create such archives or be such a
+   naive extractor.
+
+   The return value can be a suffix of FILE_NAME or a string
+   literal. */
+static const char *
+strip_antisocial_prefixes (const char *file_name)
+{
+  while (*file_name == '/'
+         || !memcmp (file_name, "./", 2)
+         || !memcmp (file_name, "../", 3))
+    file_name = strchr (file_name, '/') + 1;
+  return *file_name == '\0' || !strcmp (file_name, "..") ? "." : file_name;
+}
+
+/* Composes HEADER as a USTAR_HEADER_SIZE (512)-byte archive
+   header in ustar format for a SIZE-byte file named FILE_NAME of
+   the given TYPE.  The caller is responsible for writing the
+   header to a file or device.
+
+   If successful, returns true.  On failure (due to an
+   excessively long file name), returns false. */
+bool
+ustar_make_header (const char *file_name, enum ustar_type type,
+                   int size, char header[USTAR_HEADER_SIZE])
+{
+  struct ustar_header *h = (struct ustar_header *) header;
+
+  ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE);
+  ASSERT (type == USTAR_REGULAR || type == USTAR_DIRECTORY);
+
+  /* Check file name. */
+  file_name = strip_antisocial_prefixes (file_name);
+  if (strlen (file_name) > 99)
+    {
+      printf ("%s: file name too long\n", file_name);
+      return false;
+    }
+
+  /* Fill in header except for final checksum. */
+  memset (h, 0, sizeof *h);
+  strlcpy (h->name, file_name, sizeof h->name);
+  snprintf (h->mode, sizeof h->mode, "%07o",
+            type == USTAR_REGULAR ? 0644 : 0755);
+  strlcpy (h->uid, "0000000", sizeof h->uid);
+  strlcpy (h->gid, "0000000", sizeof h->gid);
+  snprintf (h->size, sizeof h->size, "%011o", size);
+  snprintf (h->mtime, sizeof h->size, "%011o", 1136102400);
+  h->typeflag = type;
+  strlcpy (h->magic, "ustar", sizeof h->magic);
+  h->version[0] = h->version[1] = '0';
+  strlcpy (h->gname, "root", sizeof h->gname);
+  strlcpy (h->uname, "root", sizeof h->uname);
+
+  /* Compute and fill in final checksum. */
+  snprintf (h->chksum, sizeof h->chksum, "%07o", calculate_chksum (h));
+
+  return true;
+}
+
+/* Parses a SIZE-byte octal field in S in the format used by
+   ustar format.  If successful, stores the field's value in
+   *VALUE and returns true; on failure, returns false.
+
+   ustar octal fields consist of a sequence of octal digits
+   terminated by a space or a null byte.  The ustar specification
+   seems ambiguous as to whether these fields must be padded on
+   the left with '0's, so we accept any field that fits in the
+   available space, regardless of whether it fills the space. */
+static bool
+parse_octal_field (const char *s, size_t size, unsigned long int *value)
+{
+  size_t ofs;
+
+  *value = 0;
+  for (ofs = 0; ofs < size; ofs++)
+    {
+      char c = s[ofs];
+      if (c >= '0' && c <= '7')
+        {
+          if (*value > ULONG_MAX / 8)
+            {
+              /* Overflow. */
+              return false;
+            }
+          *value = c - '0' + *value * 8;
+        }
+      else if (c == ' ' || c == '\0')
+        {
+          /* End of field, but disallow completely empty
+             fields. */
+          return ofs > 0;
+        }
+      else
+        {
+          /* Bad character. */
+          return false;
+        }
+    }
+
+  /* Field did not end in space or null byte. */
+  return false;
+}
+
+/* Returns true if the CNT bytes starting at BLOCK are all zero,
+   false otherwise. */
+static bool
+is_all_zeros (const char *block, size_t cnt)
+{
+  while (cnt-- > 0)
+    if (*block++ != 0)
+      return false;
+  return true;
+}
+
+/* Parses HEADER as a ustar-format archive header for a regular
+   file or directory.  If successful, stores the archived file's
+   name in *FILE_NAME (as a pointer into HEADER or a string
+   literal), its type in *TYPE, and its size in bytes in *SIZE,
+   and returns a null pointer.  On failure, returns a
+   human-readable error message. */
+const char *
+ustar_parse_header (const char header[USTAR_HEADER_SIZE],
+                    const char **file_name, enum ustar_type *type, int *size)
+{
+  const struct ustar_header *h = (const struct ustar_header *) header;
+  unsigned long int chksum, size_ul;
+
+  ASSERT (sizeof (struct ustar_header) == USTAR_HEADER_SIZE);
+
+  /* Detect end of archive. */
+  if (is_all_zeros (header, USTAR_HEADER_SIZE))
+    {
+      *file_name = NULL;
+      *type = USTAR_EOF;
+      *size = 0;
+      return NULL;
+    }
+
+  /* Validate ustar header. */
+  if (memcmp (h->magic, "ustar", 6))
+    return "not a ustar archive";
+  else if (h->version[0] != '0' || h->version[1] != '0')
+    return "invalid ustar version";
+  else if (!parse_octal_field (h->chksum, sizeof h->chksum, &chksum))
+    return "corrupt chksum field";
+  else if (chksum != calculate_chksum (h))
+    return "checksum mismatch";
+  else if (h->name[sizeof h->name - 1] != '\0' || h->prefix[0] != '\0')
+    return "file name too long";
+  else if (h->typeflag != USTAR_REGULAR && h->typeflag != USTAR_DIRECTORY)
+    return "unimplemented file type";
+  if (h->typeflag == USTAR_REGULAR)
+    {
+      if (!parse_octal_field (h->size, sizeof h->size, &size_ul))
+        return "corrupt file size field";
+      else if (size_ul > INT_MAX)
+        return "file too large";
+    }
+  else
+    size_ul = 0;
+
+  /* Success. */
+  *file_name = strip_antisocial_prefixes (h->name);
+  *type = h->typeflag;
+  *size = size_ul;
+  return NULL;
+}
+
diff --git a/src/lib/ustar.h b/src/lib/ustar.h
new file mode 100644
index 0000000..43a5513
--- /dev/null
+++ b/src/lib/ustar.h
@@ -0,0 +1,29 @@
+#ifndef __LIB_USTAR_H
+#define __LIB_USTAR_H
+
+/* Support for the standard Posix "ustar" format.  See the
+   documentation of the "pax" utility in [SUSv3] for the the
+   "ustar" format specification. */
+
+#include <stdbool.h>
+
+/* Type of a file entry in an archive.
+   The values here are the bytes that appear in the file format.
+   Only types of interest to Pintos are listed here. */
+enum ustar_type
+  {
+    USTAR_REGULAR = '0',        /* Ordinary file. */
+    USTAR_DIRECTORY = '5',      /* Directory. */
+    USTAR_EOF = -1              /* End of archive (not an official value). */
+  };
+
+/* Size of a ustar archive header, in bytes. */
+#define USTAR_HEADER_SIZE 512
+
+bool ustar_make_header (const char *file_name, enum ustar_type,
+                        int size, char header[USTAR_HEADER_SIZE]);
+const char *ustar_parse_header (const char header[USTAR_HEADER_SIZE],
+                                const char **file_name,
+                                enum ustar_type *, int *size);
+
+#endif /* lib/ustar.h */
diff --git a/src/misc/bochs-2.2.6-big-endian.patch b/src/misc/bochs-2.2.6-big-endian.patch
new file mode 100644
index 0000000..420f69b
--- /dev/null
+++ b/src/misc/bochs-2.2.6-big-endian.patch
@@ -0,0 +1,63 @@
+diff -urp bochs-2.2.6/gdbstub.cc bochs-2.2.6.orig/gdbstub.cc
+--- bochs-2.2.6.orig/gdbstub.cc	2006-01-17 09:15:29.000000000 -0800
++++ bochs-2.2.6/gdbstub.cc	2006-04-03 13:47:39.000000000 -0700
+@@ -672,35 +672,36 @@
+             
+           case 'g':
+ #if !BX_SUPPORT_X86_64
+-            registers[0] = EAX;
+-            registers[1] = ECX;
+-            registers[2] = EDX;
+-            registers[3] = EBX;
+-            registers[4] = ESP;
+-            registers[5] = EBP;
+-            registers[6] = ESI;
+-            registers[7] = EDI;
++            WriteHostDWordToLittleEndian(registers + 0, EAX);
++            WriteHostDWordToLittleEndian(registers + 1, ECX);
++            WriteHostDWordToLittleEndian(registers + 2, EDX);
++            WriteHostDWordToLittleEndian(registers + 3, EBX);
++            WriteHostDWordToLittleEndian(registers + 4, ESP);
++            WriteHostDWordToLittleEndian(registers + 5, EBP);
++            WriteHostDWordToLittleEndian(registers + 6, ESI);
++            WriteHostDWordToLittleEndian(registers + 7, EDI);
+             if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT)
+               {
+-                 registers[8] = EIP + 1;
++                WriteHostDWordToLittleEndian(registers + 8, EIP + 1);
+               }
+             else
+               {
+-                 registers[8] = EIP;
++                WriteHostDWordToLittleEndian(registers + 8, EIP);
+               }
+-            registers[9] = BX_CPU_THIS_PTR read_eflags();
+-            registers[10] = 
+-              BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value;
+-            registers[11] = 
+-              BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].selector.value;
+-            registers[12] = 
+-              BX_CPU_THIS_PTR sregs[BX_SEG_REG_DS].selector.value;
+-            registers[13] = 
+-              BX_CPU_THIS_PTR sregs[BX_SEG_REG_ES].selector.value;
+-            registers[14] = 
+-              BX_CPU_THIS_PTR sregs[BX_SEG_REG_FS].selector.value;
+-            registers[15] = 
+-              BX_CPU_THIS_PTR sregs[BX_SEG_REG_GS].selector.value;
++            WriteHostDWordToLittleEndian(registers + 9,
++                                         BX_CPU_THIS_PTR read_eflags());
++            WriteHostDWordToLittleEndian(registers + 10,
++              BX_CPU_THIS_PTR sregs[BX_SEG_REG_CS].selector.value);
++            WriteHostDWordToLittleEndian(registers + 11,
++              BX_CPU_THIS_PTR sregs[BX_SEG_REG_SS].selector.value);
++            WriteHostDWordToLittleEndian(registers + 12,
++              BX_CPU_THIS_PTR sregs[BX_SEG_REG_DS].selector.value);
++            WriteHostDWordToLittleEndian(registers + 13,
++              BX_CPU_THIS_PTR sregs[BX_SEG_REG_ES].selector.value);
++            WriteHostDWordToLittleEndian(registers + 14,
++              BX_CPU_THIS_PTR sregs[BX_SEG_REG_FS].selector.value);
++            WriteHostDWordToLittleEndian(registers + 15,
++              BX_CPU_THIS_PTR sregs[BX_SEG_REG_GS].selector.value);
+             mem2hex((char *)registers, obuf, NUMREGSBYTES);
+ #else
+ #define PUTREG(buf, val, len) do { \
diff --git a/src/misc/bochs-2.2.6-build.sh b/src/misc/bochs-2.2.6-build.sh
new file mode 100644
index 0000000..befe3b4
--- /dev/null
+++ b/src/misc/bochs-2.2.6-build.sh
@@ -0,0 +1,41 @@
+#! /bin/sh -e
+
+if test -z "$SRCDIR" || test -z "$PINTOSDIR" || test -z "$DSTDIR"; then
+    echo "usage: env SRCDIR=<srcdir> PINTOSDIR=<srcdir> DSTDIR=<dstdir> sh $0"
+    echo "  where <srcdir> contains bochs-2.2.6.tar.gz"
+    echo "    and <pintosdir> is the root of the pintos source tree"
+    echo "    and <dstdir> is the installation prefix (e.g. /usr/local)"
+    exit 1
+fi
+
+cd /tmp
+mkdir $$
+cd $$
+mkdir bochs-2.2.6
+tar xzf $SRCDIR/bochs-2.2.6.tar.gz
+cd bochs-2.2.6
+cat $PINTOSDIR/src/misc/bochs-2.2.6-ms-extensions.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.2.6-big-endian.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.2.6-jitter.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.2.6-triple-fault.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.2.6-solaris-tty.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.2.6-page-fault-segv.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.2.6-paranoia.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.2.6-gdbstub-ENN.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.2.6-namespace.patch | patch -p1
+if test "`uname -s`" = "SunOS"; then
+    cat $PINTOSDIR/src/misc/bochs-2.2.6-solaris-link.patch | patch -p1
+fi
+CFGOPTS="--with-x --with-x11 --with-term --with-nogui --prefix=$DSTDIR --enable-cpu-level=6"
+mkdir plain &&
+        cd plain && 
+        ../configure $CFGOPTS --enable-gdb-stub && 
+        make && 
+        make install &&
+        cd ..
+mkdir with-dbg &&
+        cd with-dbg &&
+        ../configure --enable-debugger $CFGOPTS &&
+        make &&
+        cp bochs $DSTDIR/bin/bochs-dbg &&
+        cd ..
diff --git a/src/misc/bochs-2.2.6-gdbstub-ENN.patch b/src/misc/bochs-2.2.6-gdbstub-ENN.patch
new file mode 100644
index 0000000..88a8145
--- /dev/null
+++ b/src/misc/bochs-2.2.6-gdbstub-ENN.patch
@@ -0,0 +1,29 @@
+--- bochs-2.2.6/gdbstub.cc	2006-01-17 12:15:29.000000000 -0500
++++ bochs-2.2.6-patched/gdbstub.cc	2007-02-06 23:04:51.095523904 -0500
+@@ -515,7 +515,7 @@
+                         }
+                       else
+                         {
+-                           put_reply("ENN");
++                           put_reply("Eff");
+                         }
+                    }
+                  break;                    
+@@ -761,7 +761,7 @@
+               }
+             else
+               {
+-                 put_reply("ENN");
++                 put_reply("Eff");
+               }
+             break;
+             
+@@ -782,7 +782,7 @@
+               }
+             else
+               {
+-                 put_reply("ENN");
++                 put_reply("Eff");
+               }          
+             break;
+ 
diff --git a/src/misc/bochs-2.2.6-jitter.patch b/src/misc/bochs-2.2.6-jitter.patch
new file mode 100644
index 0000000..48917e0
--- /dev/null
+++ b/src/misc/bochs-2.2.6-jitter.patch
@@ -0,0 +1,61 @@
+diff -urp bochs-2.2.6/iodev/pit82c54.cc bochs-2.2.6.orig/iodev/pit82c54.cc
+--- bochs-2.2.6.orig/iodev/pit82c54.cc	2006-01-08 12:39:08.000000000 -0800
++++ bochs-2.2.6/iodev/pit82c54.cc	2006-04-03 14:00:27.000000000 -0700
+@@ -28,6 +28,7 @@
+ 
+ #include "iodev.h"
+ #include "pit82c54.h"
++#include <stdlib.h>
+ #define LOG_THIS this->
+ 
+ 
+@@ -359,7 +360,13 @@
+       case 2:
+ 	if(thisctr.count_written) {
+ 	  if(thisctr.triggerGATE || thisctr.first_pass) {
+-	    set_count(thisctr, thisctr.inlatch);
++            unsigned n = thisctr.inlatch;
++            if (jitter && n > 5) {
++                n *= (double) rand() / RAND_MAX;
++                if (n < 5)
++                    n = 5;
++            }
++	    set_count(thisctr, n);
+ 	    thisctr.next_change_time=(thisctr.count_binary-1) & 0xFFFF;
+ 	    thisctr.null_count=0;
+ 	    if(thisctr.inlatch==1) {
+diff -urp bochs-2.2.6/main.cc bochs-2.2.6.orig/main.cc
+--- bochs-2.2.6.orig/main.cc	2006-01-22 04:31:15.000000000 -0800
++++ bochs-2.2.6/main.cc	2006-04-03 14:00:54.000000000 -0700
+@@ -105,6 +105,7 @@
+ #endif
+ 
+ char *bochsrc_filename = NULL;
++int jitter = 0;
+ 
+ void bx_print_header ()
+ {
+@@ -459,6 +460,13 @@
+     else if (!strcmp ("-q", argv[arg])) {
+       SIM->get_param_enum(BXP_BOCHS_START)->set (BX_QUICK_START);
+     }
++    else if (!strcmp ("-j", argv[arg])) {
++      if (++arg >= argc) BX_PANIC(("-j must be followed by a number"));
++      else {
++        jitter = 1;
++        srand (atoi (argv[arg]));
++      }
++    }
+     else if (!strcmp ("-f", argv[arg])) {
+       if (++arg >= argc) BX_PANIC(("-f must be followed by a filename"));
+       else bochsrc_filename = argv[arg];
+diff -up /home/blp/cs140/bochs-2.2.6/bochs.h\~ /home/blp/cs140/bochs-2.2.6/bochs.h
+--- bochs-2.2.6/bochs.h.orig	2006-01-28 08:16:02.000000000 -0800
++++ bochs-2.2.6/bochs.h	2006-04-03 14:03:54.000000000 -0700
+@@ -698,4 +698,6 @@ int bx_init_hardware ();
+ 
+ #endif
+ 
++extern int jitter;
++
+ #endif  /* BX_BOCHS_H */
diff --git a/src/misc/bochs-2.2.6-ms-extensions.patch b/src/misc/bochs-2.2.6-ms-extensions.patch
new file mode 100644
index 0000000..a6e50a0
--- /dev/null
+++ b/src/misc/bochs-2.2.6-ms-extensions.patch
@@ -0,0 +1,14 @@
+diff -urp orig/bochs-2.1.1/gui/Makefile.in bochs-2.1.1/gui/Makefile.in
+--- orig/bochs-2.1.1/gui/Makefile.in	2003-11-28 07:07:28.000000000 -0800
++++ bochs-2.1.1/gui/Makefile.in	2004-09-13 15:05:09.402039000 -0700
+@@ -44,7 +44,7 @@ SHELL = /bin/sh
+ @SET_MAKE@
+ 
+ CXX = @CXX@
+-CXXFLAGS = $(BX_INCDIRS) @CXXFLAGS@  @GUI_CXXFLAGS@
++CXXFLAGS = $(BX_INCDIRS) @CXXFLAGS@  @GUI_CXXFLAGS@ -fms-extensions
+ LOCAL_CXXFLAGS =
+ LDFLAGS = @LDFLAGS@
+ LIBS = @LIBS@
+
+
diff --git a/src/misc/bochs-2.2.6-namespace.patch b/src/misc/bochs-2.2.6-namespace.patch
new file mode 100644
index 0000000..5f03814
--- /dev/null
+++ b/src/misc/bochs-2.2.6-namespace.patch
@@ -0,0 +1,10 @@
+--- bochs-2.2.6/bx_debug/symbols.cc	2011-08-19 11:04:11.760139836 -0600
++++ bochs-2.2.6-patched/bx_debug/symbols.cc	2011-08-19 11:04:04.980139837 -0600
+@@ -92,6 +92,7 @@
+ #endif
+ 
+ using namespace std;
++namespace std { using namespace __gnu_cxx; }
+ 
+ struct symbol_entry_t
+ {
diff --git a/src/misc/bochs-2.2.6-page-fault-segv.patch b/src/misc/bochs-2.2.6-page-fault-segv.patch
new file mode 100644
index 0000000..7c61a37
--- /dev/null
+++ b/src/misc/bochs-2.2.6-page-fault-segv.patch
@@ -0,0 +1,79 @@
+Index: bochs-2.2.6/cpu/exception.cc
+diff -u bochs-2.2.6/cpu/exception.cc\~ bochs-2.2.6/cpu/exception.cc
+--- bochs-2.2.6/cpu/exception.cc~ 2006-09-28 15:51:39.000000000 -0700
++++ bochs-2.2.6/cpu/exception.cc 2006-12-08 11:14:33.000000000 -0800
+@@ -1033,6 +1033,10 @@ void BX_CPU_C::exception(unsigned vector
+     BX_CPU_THIS_PTR curr_exception[0] = exception_type;
+   }
+ 
++#if BX_GDBSTUB
++  bx_gdbstub_exception(vector);
++#endif
++
+ #if BX_CPU_LEVEL >= 2
+   if (!real_mode()) {
+     BX_CPU_THIS_PTR interrupt(vector, 0, push_error, error_code);
+Index: bochs-2.2.6/gdbstub.cc
+diff -u bochs-2.2.6/gdbstub.cc\~ bochs-2.2.6/gdbstub.cc
+--- bochs-2.2.6/gdbstub.cc~ 2006-09-28 15:51:39.000000000 -0700
++++ bochs-2.2.6/gdbstub.cc 2006-12-08 11:12:03.000000000 -0800
+@@ -26,6 +26,7 @@ static int last_stop_reason = GDBSTUB_ST
+ #define GDBSTUB_EXECUTION_BREAKPOINT    (0xac1)
+ #define GDBSTUB_TRACE                   (0xac2)
+ #define GDBSTUB_USER_BREAK              (0xac3)
++#define GDBSTUB_EXCEPTION_0E            (0xac4)
+ 
+ static int listen_socket_fd;
+ static int socket_fd;
+@@ -271,6 +272,12 @@ int bx_gdbstub_check(unsigned int eip)
+    return(GDBSTUB_STOP_NO_REASON);
+ }
+ 
++void bx_gdbstub_exception(unsigned int nr) 
++{
++    if (nr == 0x0e)
++        last_stop_reason = GDBSTUB_EXCEPTION_0E;
++}
++
+ static int remove_breakpoint(unsigned int addr, int len)
+ {
+    unsigned int i;
+@@ -452,6 +459,10 @@ static void debug_loop(void)
+                    {
+                       write_signal(&buf[1], SIGTRAP);
+                    }
++                 else if (last_stop_reason == GDBSTUB_EXCEPTION_0E)
++                   {
++                      write_signal(&buf[1], SIGSEGV);
++                   }
+                  else
+                    {
+                       write_signal(&buf[1], 0);
+@@ -476,10 +487,14 @@ static void debug_loop(void)
+                    {
+                       write_signal(&buf[1], SIGTRAP);
+                    }
+-                 else
++                 else if (last_stop_reason == GDBSTUB_EXCEPTION_0E)
+                    {
+                       write_signal(&buf[1], SIGSEGV);
+                    }
++                 else
++                   {
++                      write_signal(&buf[1], 0);
++                   }
+                  put_reply(buf);
+                  break;
+               }
+Index: bochs-2.2.6/bochs.h
+diff -u bochs-2.2.6/bochs.h\~ bochs-2.2.6/bochs.h
+--- bochs-2.2.6/bochs.h~ 2006-09-28 15:51:39.000000000 -0700
++++ bochs-2.2.6/bochs.h 2006-12-08 11:14:19.000000000 -0800
+@@ -375,6 +375,7 @@ BOCHSAPI extern logfunc_t *genlog;
+ // defines for GDB stub
+ void bx_gdbstub_init(int argc, char* argv[]);
+ int bx_gdbstub_check(unsigned int eip);
++void bx_gdbstub_exception(unsigned int nr);
+ #define GDBSTUB_STOP_NO_REASON   (0xac0)
+ 
+ #if BX_SUPPORT_SMP
diff --git a/src/misc/bochs-2.2.6-paranoia.patch b/src/misc/bochs-2.2.6-paranoia.patch
new file mode 100644
index 0000000..ff8d736
--- /dev/null
+++ b/src/misc/bochs-2.2.6-paranoia.patch
@@ -0,0 +1,19 @@
+Index: bochs-2.2.6/iodev/hdimage.h
+diff -u bochs-2.2.6/iodev/hdimage.h\~ bochs-2.2.6/iodev/hdimage.h
+--- bochs-2.2.6/iodev/hdimage.h~ 2005-11-06 03:07:01.000000000 -0800
++++ bochs-2.2.6/iodev/hdimage.h 2006-09-28 15:55:50.000000000 -0700
+@@ -273,14 +273,8 @@ class sparse_image_t : public device_ima
+ 
+  void panic(const char * message);
+  off_t
+-#ifndef PARANOID
+-       sparse_image_t::
+-#endif
+                        get_physical_offset();
+  void
+-#ifndef PARANOID
+-       sparse_image_t::
+-#endif
+                        set_virtual_page(Bit32u new_virtual_page);
+  void read_header();
+  ssize_t read_page_fragment(Bit32u read_virtual_page, Bit32u read_page_offset, size_t read_size, void * buf);
diff --git a/src/misc/bochs-2.2.6-solaris-link.patch b/src/misc/bochs-2.2.6-solaris-link.patch
new file mode 100644
index 0000000..5afef49
--- /dev/null
+++ b/src/misc/bochs-2.2.6-solaris-link.patch
@@ -0,0 +1,11 @@
+--- bochs-2.2.6.orig/Makefile.in	2006-04-03 16:34:51.170387000 -0700
++++ bochs-2.2.6/Makefile.in	2006-04-03 16:34:57.480303000 -0700
+@@ -93,7 +93,7 @@
+ CFLAGS = @CFLAGS@ @GUI_CFLAGS@ $(MCH_CFLAGS) $(FLA_FLAGS) @DEFINE_PLUGIN_PATH@ -DBX_SHARE_PATH='"$(sharedir)"'
+ CXXFLAGS = @CXXFLAGS@ @GUI_CXXFLAGS@ $(MCH_CFLAGS) $(FLA_FLAGS) @DEFINE_PLUGIN_PATH@ -DBX_SHARE_PATH='"$(sharedir)"'
+ 
+-LDFLAGS = @LDFLAGS@
++LDFLAGS = @LDFLAGS@ -lsocket
+ LIBS = @LIBS@
+ # To compile with readline:
+ #   linux needs just -lreadline
diff --git a/src/misc/bochs-2.2.6-solaris-tty.patch b/src/misc/bochs-2.2.6-solaris-tty.patch
new file mode 100644
index 0000000..a9725dc
--- /dev/null
+++ b/src/misc/bochs-2.2.6-solaris-tty.patch
@@ -0,0 +1,31 @@
+--- bochs-2.2.6/iodev/serial.cc	2005-07-11 09:24:47.000000000 -0700
++++ bochs-2.2.6.patch/iodev/serial.cc	2006-05-28 16:41:33.278938000 -0700
+@@ -245,8 +245,13 @@
+             BX_SER_THIS s[i].io_mode = BX_SER_MODE_TERM;
+             BX_DEBUG(("com%d tty_id: %d", i+1, BX_SER_THIS s[i].tty_id));
+             tcgetattr(BX_SER_THIS s[i].tty_id, &BX_SER_THIS s[i].term_orig);
+-            bcopy((caddr_t) &BX_SER_THIS s[i].term_orig, (caddr_t) &BX_SER_THIS s[i].term_new, sizeof(struct termios));
+-            cfmakeraw(&BX_SER_THIS s[i].term_new);
++            memcpy((caddr_t) &BX_SER_THIS s[i].term_new, (caddr_t) &BX_SER_THIS s[i].term_orig, sizeof(struct termios));
++            BX_SER_THIS s[i].term_new.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
++                                          |INLCR|IGNCR|ICRNL|IXON);
++            BX_SER_THIS s[i].term_new.c_oflag &= ~OPOST;
++            BX_SER_THIS s[i].term_new.c_lflag &= ~(ECHO|ECHONL|ICANON|ISIG|IEXTEN);
++            BX_SER_THIS s[i].term_new.c_cflag &= ~(CSIZE|PARENB);
++            BX_SER_THIS s[i].term_new.c_cflag |= CS8;
+             BX_SER_THIS s[i].term_new.c_oflag |= OPOST | ONLCR;  // Enable NL to CR-NL translation
+ #ifndef TRUE_CTLC
+             // ctl-C will exit Bochs, or trap to the debugger
+
+
+--- bochs-2.2.6/iodev/serial.h	2005-07-10 09:51:09.000000000 -0700
++++ bochs-2.2.6.patch/iodev/serial.h	2006-05-28 16:39:03.757839000 -0700
+@@ -40,7 +40,7 @@
+ #define SERIAL_ENABLE
+ #endif
+ 
+-#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || defined(__APPLE__)
++#if defined(__NetBSD__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__linux__) || defined(__GNU__) || defined(__GLIBC__) || defined(__APPLE__) || defined(__sun__)
+ #define SERIAL_ENABLE
+ extern "C" {
+ #include <termios.h>
diff --git a/src/misc/bochs-2.2.6-triple-fault.patch b/src/misc/bochs-2.2.6-triple-fault.patch
new file mode 100644
index 0000000..f6d0871
--- /dev/null
+++ b/src/misc/bochs-2.2.6-triple-fault.patch
@@ -0,0 +1,57 @@
+diff -u bochs-2.2.6.orig/cpu/exception.cc bochs-2.2.6/cpu/exception.cc
+--- bochs-2.2.6.orig/cpu/exception.cc
++++ bochs-2.2.6/cpu/exception.cc
+@@ -841,6 +841,13 @@ void BX_CPU_C::exception(unsigned vector
+ 
+   BX_CPU_THIS_PTR errorno++;
+   if (BX_CPU_THIS_PTR errorno >= 3) {
++#if BX_GDBSTUB
++    if (bx_dbg.gdbstub_enabled) {
++        fprintf(stderr, "Triple fault: stopping for gdb\n");
++        BX_CPU_THIS_PTR ispanic = 1;
++        longjmp(BX_CPU_THIS_PTR jmp_buf_env, 1);
++    }
++#endif
+ #if BX_RESET_ON_TRIPLE_FAULT
+     BX_ERROR(("exception(): 3rd (%d) exception with no resolution, shutdown status is %02xh, resetting", vector, DEV_cmos_get_reg(0x0f)));
+     debug(BX_CPU_THIS_PTR prev_eip);
+@@ -860,6 +867,13 @@ void BX_CPU_C::exception(unsigned vector
+ 
+   /* if 1st was a double fault (software INT?), then shutdown */
+   if ( (BX_CPU_THIS_PTR errorno==2) && (BX_CPU_THIS_PTR curr_exception[0]==BX_ET_DOUBLE_FAULT) ) {
++#if BX_GDBSTUB
++    if (bx_dbg.gdbstub_enabled) {
++        fprintf(stderr, "Triple fault: stopping for gdb\n");
++        BX_CPU_THIS_PTR ispanic = 1;
++        longjmp(BX_CPU_THIS_PTR jmp_buf_env, 1);
++    }
++#endif
+ #if BX_RESET_ON_TRIPLE_FAULT
+     BX_INFO(("exception(): triple fault encountered, shutdown status is %02xh, resetting", DEV_cmos_get_reg(0x0f)));
+     debug(BX_CPU_THIS_PTR prev_eip);
+diff -u bochs-2.2.6.orig/gdbstub.cc bochs-2.2.6/gdbstub.cc
+--- bochs-2.2.6.orig/gdbstub.cc
++++ bochs-2.2.6/gdbstub.cc
+@@ -466,19 +466,19 @@ static void debug_loop(void)
+                  
+                  BX_INFO (("stepping"));
+                  stub_trace_flag = 1;
++                 bx_cpu.ispanic = 0;
+                  bx_cpu.cpu_loop(-1);
+                  DEV_vga_refresh();
+                  stub_trace_flag = 0;
+                  BX_INFO (("stopped with %x", last_stop_reason));
+                  buf[0] = 'S';
+-                 if (last_stop_reason == GDBSTUB_EXECUTION_BREAKPOINT ||
+-                     last_stop_reason == GDBSTUB_TRACE)
++                 if (last_stop_reason == GDBSTUB_TRACE && !bx_cpu.ispanic)
+                    {
+                       write_signal(&buf[1], SIGTRAP);
+                    }
+                  else
+                    {
+-                      write_signal(&buf[1], SIGTRAP);
++                      write_signal(&buf[1], SIGSEGV);
+                    }
+                  put_reply(buf);
+                  break;
diff --git a/src/misc/bochs-2.6.11-banner-stderr.patch b/src/misc/bochs-2.6.11-banner-stderr.patch
new file mode 100644
index 0000000..1e0af45
--- /dev/null
+++ b/src/misc/bochs-2.6.11-banner-stderr.patch
@@ -0,0 +1,26 @@
+--- bochs-2.6.11.dist/main.cc	2020-01-02 08:19:02.083668000 -0800
++++ bochs-2.6.11/main.cc	2021-01-17 17:17:17.526508739 -0800
+@@ -118,18 +118,18 @@
+ {
+   char buffer[128];
+ 
+-  printf("%s\n", divider);
++  fprintf(stderr, "%s\n", divider);
+   sprintf (buffer, "Bochs x86 Emulator %s\n", VERSION);
+-  bx_center_print(stdout, buffer, 72);
++  bx_center_print(stderr, buffer, 72);
+   if (REL_STRING[0]) {
+     sprintf(buffer, "%s\n", REL_STRING);
+-    bx_center_print(stdout, buffer, 72);
++    bx_center_print(stderr, buffer, 72);
+     if (bx_get_timestamp(buffer) > 0) {
+-      bx_center_print(stdout, buffer, 72);
++      bx_center_print(stderr, buffer, 72);
+       printf("\n");
+     }
+   }
+-  printf("%s\n", divider);
++  fprintf(stderr, "%s\n", divider);
+ }
+ 
+ #if BX_WITH_CARBON
diff --git a/src/misc/bochs-2.6.11-build.sh b/src/misc/bochs-2.6.11-build.sh
new file mode 100644
index 0000000..603bf1a
--- /dev/null
+++ b/src/misc/bochs-2.6.11-build.sh
@@ -0,0 +1,49 @@
+#!/bin/sh -xe
+
+# Note: as of 2.6.9, you need to change line 566 of pintos.  Replace:
+#     user_shortcut: keys=ctrlaltdel
+# with
+#     keyboard: user_shortcut=ctrl-alt-del
+# to accommodate the new bochsrc.txt syntax
+
+V=2.6.11
+
+if test 1 != "$#"; then
+    echo "usage: $0 <install-prefix>" >&2
+    exit 2
+fi
+export PREFIX="$1"
+
+SRCDIR=$(dirname $(readlink -f "$0"))
+if test ! -d "$SRCDIR"; then
+    echo "cannot file src directory" >&2
+    exit 1
+fi
+
+if test ! -f "$SRCDIR/bochs-$V.tar.gz"; then
+    wget -O "$SRCDIR/bochs-$V.tar.gz" \
+	 "https://sourceforge.net/projects/bochs/files/bochs/$V/bochs-$V.tar.gz/download"
+fi
+
+builddir=$(mktemp -d /tmp/bochsXXXXXXXXX)
+trap "rm -rf \"$builddir\"" 0
+
+cd "$builddir"
+tar xzf "$SRCDIR/bochs-$V.tar.gz"
+cd "bochs-$V"
+
+for patchfile in "$SRCDIR/bochs-$V"-*.patch; do
+    patch -p1 -i "$patchfile"
+done
+
+CFGOPTS="--with-x --with-x11 --with-term --with-nogui"
+mkdir plain
+(cd plain &&
+        ../configure $CFGOPTS --prefix="$PREFIX" --enable-gdb-stub &&
+        make -j$(nproc) &&
+        make install)
+mkdir with-dbg
+(cd with-dbg &&
+        ../configure --enable-debugger --disable-debugger-gui $CFGOPTS --prefix="$PREFIX" &&
+        make -j$(nproc) &&
+        cp bochs "$PREFIX/bin/bochs-dbg")
diff --git a/src/misc/bochs-2.6.11-jitter-plus-segv.patch b/src/misc/bochs-2.6.11-jitter-plus-segv.patch
new file mode 100644
index 0000000..363196f
--- /dev/null
+++ b/src/misc/bochs-2.6.11-jitter-plus-segv.patch
@@ -0,0 +1,135 @@
+diff -ru bochs-2.6.11.dist/bochs.h bochs-2.6.11/bochs.h
+--- bochs-2.6.11.dist/bochs.h	2019-12-19 23:42:07.299552000 -0800
++++ bochs-2.6.11/bochs.h	2021-01-17 17:28:08.093933541 -0800
+@@ -409,6 +409,7 @@
+ void bx_gdbstub_init(void);
+ void bx_gdbstub_break(void);
+ int bx_gdbstub_check(unsigned int eip);
++void bx_gdbstub_exception(unsigned int nr);
+ #define GDBSTUB_STOP_NO_REASON   (0xac0)
+ 
+ #if BX_SUPPORT_SMP
+@@ -664,4 +665,6 @@
+ 
+ #endif
+ 
++extern int jitter;
++
+ #endif  /* BX_BOCHS_H */
+Only in bochs-2.6.11: bochs.h.orig
+diff -ru bochs-2.6.11.dist/cpu/exception.cc bochs-2.6.11/cpu/exception.cc
+--- bochs-2.6.11.dist/cpu/exception.cc	2019-12-26 08:48:33.074097000 -0800
++++ bochs-2.6.11/cpu/exception.cc	2021-01-17 17:28:08.093933541 -0800
+@@ -991,6 +991,10 @@
+ 
+   BX_CPU_THIS_PTR last_exception_type = exception_type;
+ 
++#if BX_GDBSTUB
++  bx_gdbstub_exception(vector);
++#endif
++
+   if (real_mode()) {
+     push_error = 0; // not INT, no error code pushed
+     error_code = 0;
+Only in bochs-2.6.11/cpu: exception.cc.orig
+diff -ru bochs-2.6.11.dist/gdbstub.cc bochs-2.6.11/gdbstub.cc
+--- bochs-2.6.11.dist/gdbstub.cc	2017-03-06 13:30:05.467768000 -0800
++++ bochs-2.6.11/gdbstub.cc	2021-01-17 17:28:35.270952903 -0800
+@@ -50,6 +50,7 @@
+ #define GDBSTUB_EXECUTION_BREAKPOINT    (0xac1)
+ #define GDBSTUB_TRACE                   (0xac2)
+ #define GDBSTUB_USER_BREAK              (0xac3)
++#define GDBSTUB_EXCEPTION_0E            (0xac4)
+ 
+ static bx_list_c *gdbstub_list;
+ static int listen_socket_fd;
+@@ -319,6 +320,12 @@
+   return GDBSTUB_STOP_NO_REASON;
+ }
+ 
++void bx_gdbstub_exception(unsigned int nr)
++{
++    if (nr == 0x0e)
++        last_stop_reason = GDBSTUB_EXCEPTION_0E;
++}
++
+ static int remove_breakpoint(Bit64u addr, int len)
+ {
+   if (len != 1)
+@@ -489,6 +496,10 @@
+         {
+           write_signal(&buf[1], SIGTRAP);
+         }
++        else if (last_stop_reason == GDBSTUB_EXCEPTION_0E)
++        {
++          write_signal(&buf[1], SIGSEGV);
++        }
+         else
+         {
+           write_signal(&buf[1], 0);
+@@ -516,6 +527,10 @@
+         {
+           write_signal(&buf[1], SIGTRAP);
+         }
++        else if (last_stop_reason == GDBSTUB_EXCEPTION_0E)
++        {
++          write_signal(&buf[1], SIGSEGV);
++        }
+         else
+         {
+           write_signal(&buf[1], SIGTRAP);
+Only in bochs-2.6.11: gdbstub.cc.orig
+Only in bochs-2.6.11: gdbstub.cc~
+diff -ru bochs-2.6.11.dist/iodev/pit82c54.cc bochs-2.6.11/iodev/pit82c54.cc
+--- bochs-2.6.11.dist/iodev/pit82c54.cc	2018-05-14 11:17:04.955283000 -0700
++++ bochs-2.6.11/iodev/pit82c54.cc	2021-01-17 17:28:08.097266918 -0800
+@@ -49,6 +49,7 @@
+ 
+ #include "iodev.h"
+ #include "pit82c54.h"
++#include <stdlib.h>
+ #define LOG_THIS this->
+ 
+ 
+@@ -410,7 +411,14 @@
+       case 2:
+         if (thisctr.count_written) {
+           if (thisctr.triggerGATE || thisctr.first_pass) {
+-            set_count(thisctr, thisctr.inlatch);
++            //set_count(thisctr, thisctr.inlatch);
++            unsigned n = thisctr.inlatch;
++            if (jitter && n > 5) {
++                n *= (double) rand() / RAND_MAX;
++                if (n < 5)
++                    n = 5;
++            }
++            set_count(thisctr, n);
+             thisctr.next_change_time=(thisctr.count_binary-1) & 0xFFFF;
+             thisctr.null_count=0;
+             if (thisctr.inlatch==1) {
+diff -ru bochs-2.6.11.dist/main.cc bochs-2.6.11/main.cc
+--- bochs-2.6.11.dist/main.cc	2020-01-02 08:19:02.083668000 -0800
++++ bochs-2.6.11/main.cc	2021-01-17 17:28:08.097266918 -0800
+@@ -94,6 +94,7 @@
+ BOCHSAPI BX_MEM_C bx_mem;
+ 
+ char *bochsrc_filename = NULL;
++int jitter = 0;
+ 
+ size_t bx_get_timestamp(char *buffer)
+ {
+@@ -705,6 +706,13 @@
+       else SIM->get_param_string(BXPN_DEBUGGER_LOG_FILENAME)->set(argv[arg]);
+     }
+ #endif
++    else if (!strcmp ("-j", argv[arg])) {
++      if (++arg >= argc) BX_PANIC(("-j must be followed by a number"));
++      else {
++        jitter = 1;
++        srand (atoi (argv[arg]));
++      }
++    }
+     else if (!strcmp("-f", argv[arg])) {
+       if (++arg >= argc) BX_PANIC(("-f must be followed by a filename"));
+       else bochsrc_filename = argv[arg];
+Only in bochs-2.6.11: main.cc.orig
diff --git a/src/misc/bochs-2.6.11-link-tinfo.patch b/src/misc/bochs-2.6.11-link-tinfo.patch
new file mode 100644
index 0000000..ea138d0
--- /dev/null
+++ b/src/misc/bochs-2.6.11-link-tinfo.patch
@@ -0,0 +1,22 @@
+diff -ru bochs-2.6.11.dist/configure bochs-2.6.11/configure
+--- bochs-2.6.11.dist/configure	2020-01-04 23:37:34.155348000 -0800
++++ bochs-2.6.11/configure	2021-01-17 17:30:01.878741469 -0800
+@@ -25331,7 +25331,7 @@
+   $as_echo_n "(cached) " >&6
+ else
+   ac_check_lib_save_LIBS=$LIBS
+-LIBS="-lncurses  $LIBS"
++LIBS="-lncurses -ltinfo $LIBS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ /* end confdefs.h.  */
+ 
+@@ -25523,7 +25523,7 @@
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_mvaddch" >&5
+ $as_echo "$ac_cv_lib_ncurses_mvaddch" >&6; }
+ if test "x$ac_cv_lib_ncurses_mvaddch" = xyes; then :
+-  GUI_LINK_OPTS_TERM='-lncurses'
++  GUI_LINK_OPTS_TERM='-lncurses -ltinfo'
+ fi
+ 
+     { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mvaddch in -ltermlib" >&5
+Only in bochs-2.6.11: configure.orig
diff --git a/src/misc/bochs-2.6.2-banner-stderr.patch b/src/misc/bochs-2.6.2-banner-stderr.patch
new file mode 100644
index 0000000..8587142
--- /dev/null
+++ b/src/misc/bochs-2.6.2-banner-stderr.patch
@@ -0,0 +1,32 @@
+diff -Naur bochs-2.6.2/main.cc bochs-2.6.2.new/main.cc
+--- bochs-2.6.2/main.cc	2014-01-06 23:29:42.000000001 -0800
++++ bochs-2.6.2.new/main.cc	2014-01-06 22:33:42.000000001 -0800
+@@ -105,23 +105,23 @@
+ 
+ void bx_print_header()
+ {
+-  printf("%s\n", divider);
++  fprintf(stderr, "%s\n", divider);
+   char buffer[128];
+   sprintf (buffer, "Bochs x86 Emulator %s\n", VER_STRING);
+-  bx_center_print(stdout, buffer, 72);
++  bx_center_print(stderr, buffer, 72);
+   if (REL_STRING[0]) {
+     sprintf(buffer, "%s\n", REL_STRING);
+-    bx_center_print(stdout, buffer, 72);
++    bx_center_print(stderr, buffer, 72);
+ #ifdef __DATE__
+ #ifdef __TIME__
+     sprintf(buffer, "Compiled on %s at %s\n", __DATE__, __TIME__);
+ #else
+     sprintf(buffer, "Compiled on %s\n", __DATE__);
+ #endif
+-    bx_center_print(stdout, buffer, 72);
++    bx_center_print(stderr, buffer, 72);
+ #endif
+   }
+-  printf("%s\n", divider);
++  fprintf(stderr, "%s\n", divider);
+ }
+ 
+ #if BX_WITH_CARBON
diff --git a/src/misc/bochs-2.6.2-block-device-check.patch b/src/misc/bochs-2.6.2-block-device-check.patch
new file mode 100644
index 0000000..a0b930c
--- /dev/null
+++ b/src/misc/bochs-2.6.2-block-device-check.patch
@@ -0,0 +1,12 @@
+diff -Naur bochs-2.6.2/iodev/hdimage/hdimage.cc bochs-2.6.2.new/iodev/hdimage/hdimage.cc
+--- bochs-2.6.2/iodev/hdimage/hdimage.cc	2013-03-08 10:25:32.000000001 -0800
++++ bochs-2.6.2.new/iodev/hdimage/hdimage.cc	2014-01-10 06:54:24.000000001 -0800
+@@ -200,7 +200,7 @@
+       return -1;
+     }
+ #ifdef linux
+-    if (stat_buf.st_rdev) { // Is this a special device file (e.g. /dev/sde) ?
++    if (S_ISBLK(stat_buf.st_mode)) { // Is this a special device file (e.g. /dev/sde) ?
+       ioctl(fd, BLKGETSIZE64, fsize); // yes it's!
+     }
+     else
diff --git a/src/misc/bochs-2.6.2-build.sh b/src/misc/bochs-2.6.2-build.sh
new file mode 100644
index 0000000..ce90a71
--- /dev/null
+++ b/src/misc/bochs-2.6.2-build.sh
@@ -0,0 +1,34 @@
+#! /bin/sh -e
+
+if test -z "$SRCDIR" || test -z "$PINTOSDIR" || test -z "$DSTDIR"; then
+    echo "usage: env SRCDIR=<srcdir> PINTOSDIR=<srcdir> DSTDIR=<dstdir> sh $0"
+    echo "  where <srcdir> contains bochs-2.6.2.tar.gz"
+    echo "    and <pintosdir> is the root of the pintos source tree"
+    echo "    and <dstdir> is the installation prefix (e.g. /usr/local)"
+    exit 1
+fi
+
+cd /tmp
+mkdir $$
+cd $$
+mkdir bochs-2.6.2
+tar xzf $SRCDIR/bochs-2.6.2.tar.gz
+cd bochs-2.6.2
+cat $PINTOSDIR/src/misc/bochs-2.6.2-jitter-plus-segv.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.6.2-xrandr-pkgconfig.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.6.2-banner-stderr.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.6.2-block-device-check.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.6.2-link-tinfo.patch | patch -p1
+CFGOPTS="--with-x --with-x11 --with-term --with-nogui --prefix=$DSTDIR"
+mkdir plain &&
+        cd plain && 
+        ../configure $CFGOPTS --enable-gdb-stub && 
+        make && 
+        make install &&
+        cd ..
+mkdir with-dbg &&
+        cd with-dbg &&
+        ../configure --enable-debugger --disable-debugger-gui $CFGOPTS &&
+        make &&
+        cp bochs $DSTDIR/bin/bochs-dbg &&
+        cd ..
diff --git a/src/misc/bochs-2.6.2-jitter-plus-segv.patch b/src/misc/bochs-2.6.2-jitter-plus-segv.patch
new file mode 100644
index 0000000..b6ad247
--- /dev/null
+++ b/src/misc/bochs-2.6.2-jitter-plus-segv.patch
@@ -0,0 +1,135 @@
+diff --git a/bochs.h b/bochs.h
+index c2d6c6b..d37cf12 100644
+--- a/bochs.h
++++ b/bochs.h
+@@ -392,6 +392,7 @@ BOCHSAPI extern logfunc_t *genlog;
+ void bx_gdbstub_init(void);
+ void bx_gdbstub_break(void);
+ int bx_gdbstub_check(unsigned int eip);
++void bx_gdbstub_exception(unsigned int nr);
+ #define GDBSTUB_STOP_NO_REASON   (0xac0)
+ 
+ #if BX_SUPPORT_SMP
+@@ -589,4 +590,6 @@ BX_CPP_INLINE Bit64u bx_bswap64(Bit64u val64)
+ #define CopyHostQWordLittleEndian(hostAddrDst,  hostAddrSrc) \
+     (* (Bit64u *)(hostAddrDst)) = (* (Bit64u *)(hostAddrSrc));
+ 
++extern int jitter;
++
+ #endif  /* BX_BOCHS_H */
+diff --git a/cpu/exception.cc b/cpu/exception.cc
+index db38d1b..b2c5f29 100644
+--- a/cpu/exception.cc
++++ b/cpu/exception.cc
+@@ -914,6 +914,10 @@ void BX_CPU_C::exception(unsigned vector, Bit16u error_code)
+ 
+   BX_CPU_THIS_PTR last_exception_type = exception_type;
+ 
++#if BX_GDBSTUB
++  bx_gdbstub_exception(vector);
++#endif
++
+   if (real_mode()) {
+     push_error = 0; // not INT, no error code pushed
+     error_code = 0;
+diff --git a/gdbstub.cc b/gdbstub.cc
+index da600b4..577938d 100644
+--- a/gdbstub.cc
++++ b/gdbstub.cc
+@@ -49,6 +49,7 @@ static int last_stop_reason = GDBSTUB_STOP_NO_REASON;
+ #define GDBSTUB_EXECUTION_BREAKPOINT    (0xac1)
+ #define GDBSTUB_TRACE                   (0xac2)
+ #define GDBSTUB_USER_BREAK              (0xac3)
++#define GDBSTUB_EXCEPTION_0E            (0xac4)
+ 
+ static bx_list_c *gdbstub_list;
+ static int listen_socket_fd;
+@@ -317,6 +318,12 @@ int bx_gdbstub_check(unsigned int eip)
+   return GDBSTUB_STOP_NO_REASON;
+ }
+ 
++void bx_gdbstub_exception(unsigned int nr) 
++{
++    if (nr == 0x0e)
++        last_stop_reason = GDBSTUB_EXCEPTION_0E;
++}
++
+ static int remove_breakpoint(unsigned addr, int len)
+ {
+   if (len != 1)
+@@ -487,6 +494,10 @@ static void debug_loop(void)
+         {
+           write_signal(&buf[1], SIGTRAP);
+         }
++        else if (last_stop_reason == GDBSTUB_EXCEPTION_0E)
++        {
++          write_signal(&buf[1], SIGSEGV);
++        }
+         else
+         {
+           write_signal(&buf[1], 0);
+@@ -514,6 +525,10 @@ static void debug_loop(void)
+         {
+           write_signal(&buf[1], SIGTRAP);
+         }
++        else if (last_stop_reason == GDBSTUB_EXCEPTION_0E)
++        {
++          write_signal(&buf[1], SIGSEGV);
++        }
+         else
+         {
+           write_signal(&buf[1], SIGTRAP);
+diff --git a/iodev/pit82c54.cc b/iodev/pit82c54.cc
+index 09dcd8e..7e335d4 100644
+--- a/iodev/pit82c54.cc
++++ b/iodev/pit82c54.cc
+@@ -49,6 +49,7 @@
+ 
+ #include "iodev.h"
+ #include "pit82c54.h"
++#include <stdlib.h>
+ #define LOG_THIS this->
+ 
+ 
+@@ -410,7 +411,14 @@ void BX_CPP_AttrRegparmN(1) pit_82C54::clock(Bit8u cnum)
+       case 2:
+         if (thisctr.count_written) {
+           if (thisctr.triggerGATE || thisctr.first_pass) {
+-            set_count(thisctr, thisctr.inlatch);
++            //set_count(thisctr, thisctr.inlatch);
++            unsigned n = thisctr.inlatch;
++            if (jitter && n > 5) {
++                n *= (double) rand() / RAND_MAX;
++                if (n < 5)
++                    n = 5;
++            }
++            set_count(thisctr, n);
+             thisctr.next_change_time=(thisctr.count_binary-1) & 0xFFFF;
+             thisctr.null_count=0;
+             if (thisctr.inlatch==1) {
+diff --git a/main.cc b/main.cc
+index 0f11e31..008f05f 100644
+--- a/main.cc
++++ b/main.cc
+@@ -101,6 +101,7 @@ BOCHSAPI BX_CPU_C bx_cpu;
+ BOCHSAPI BX_MEM_C bx_mem;
+ 
+ char *bochsrc_filename = NULL;
++int jitter = 0;
+ 
+ void bx_print_header()
+ {
+@@ -639,6 +640,13 @@ int bx_init_main(int argc, char *argv[])
+       else SIM->get_param_string(BXPN_DEBUGGER_LOG_FILENAME)->set(argv[arg]);
+     }
+ #endif
++    else if (!strcmp ("-j", argv[arg])) {
++      if (++arg >= argc) BX_PANIC(("-j must be followed by a number"));
++      else {
++        jitter = 1;
++        srand (atoi (argv[arg]));
++      }
++    }
+     else if (!strcmp("-f", argv[arg])) {
+       if (++arg >= argc) BX_PANIC(("-f must be followed by a filename"));
+       else bochsrc_filename = argv[arg];
diff --git a/src/misc/bochs-2.6.2-link-tinfo.patch b/src/misc/bochs-2.6.2-link-tinfo.patch
new file mode 100644
index 0000000..e83047e
--- /dev/null
+++ b/src/misc/bochs-2.6.2-link-tinfo.patch
@@ -0,0 +1,22 @@
+diff --git i/configure w/configure
+index be5e0a5..99b2e14 100755
+--- i/configure
++++ w/configure
+@@ -24739,7 +24739,7 @@ if ${ac_cv_lib_ncurses_mvaddch+:} false; then :
+   $as_echo_n "(cached) " >&6
+ else
+   ac_check_lib_save_LIBS=$LIBS
+-LIBS="-lncurses  $LIBS"
++LIBS="-lncurses -ltinfo $LIBS"
+ cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+ /* end confdefs.h.  */
+
+@@ -24770,7 +24770,7 @@ fi
+ { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_ncurses_mvaddch" >&5
+ $as_echo "$ac_cv_lib_ncurses_mvaddch" >&6; }
+ if test "x$ac_cv_lib_ncurses_mvaddch" = xyes; then :
+-  GUI_LINK_OPTS_TERM='-lncurses'
++  GUI_LINK_OPTS_TERM='-lncurses -ltinfo'
+ fi
+
+   { $as_echo "$as_me:${as_lineno-$LINENO}: checking for mvaddch in -ltermlib" >&5
diff --git a/src/misc/bochs-2.6.2-xrandr-pkgconfig.patch b/src/misc/bochs-2.6.2-xrandr-pkgconfig.patch
new file mode 100644
index 0000000..b92f814
--- /dev/null
+++ b/src/misc/bochs-2.6.2-xrandr-pkgconfig.patch
@@ -0,0 +1,14 @@
+diff -Naur bochs-2.6.2.orig/configure bochs-2.6.2/configure
+--- bochs-2.6.2.orig/configure	2014-01-06 16:39:03.000000000 -0800
++++ bochs-2.6.2/configure	2014-01-06 16:39:23.000000000 -0800
+@@ -24202,8 +24202,8 @@
+   fi
+ 
+   if test "$PKGCONFIG" != not_found; then
+-    X_CFLAGS="`pkg-config --cflags x11`"
+-    X_LIBS="`pkg-config --libs x11` $XPM_LIB -lXrandr"
++    X_CFLAGS="`pkg-config --cflags x11 xrandr`"
++    X_LIBS="`pkg-config --libs x11 xrandr` $XPM_LIB"
+   else
+     X_LIBS="$X_LIBS -lX11 $XPM_LIB -lXrandr"
+   fi
diff --git a/src/misc/gcc-3.3.6-cross-howto b/src/misc/gcc-3.3.6-cross-howto
new file mode 100644
index 0000000..ad25173
--- /dev/null
+++ b/src/misc/gcc-3.3.6-cross-howto
@@ -0,0 +1,39 @@
+Here are the commands we used to build and install the SPARC
+cross-compiler:
+
+PINTOSROOT=$HOME/private/pintos
+
+PREFIX=/usr/class/cs140/`uname -m`
+PATH=$PATH:$PREFIX/bin
+TMP=`pwd`
+
+wget ftp://ftp.gnu.org/pub/gnu/binutils/binutils-2.15.tar.bz2
+wget ftp://sources.redhat.com/pub/newlib/newlib-1.13.0.tar.gz
+wget ftp://ftp.gnu.org/pub/gnu/gcc/gcc-3.3.6/gcc-core-3.3.6.tar.bz2
+wget ftp://ftp.gnu.org/pub/gnu/gdb/gdb-6.3.tar.bz2
+
+bzcat binutils-2.15.tar.bz2 | tar x
+tar xzf newlib-1.13.0.tar.gz
+bzcat gcc-core-3.3.6.tar.bz2 | tar x
+bzcat gdb-6.3.tar.bz2 | tar x
+
+cd $TMP/binutils-2.15
+mkdir i386
+cd i386
+../configure --target=i386-elf --prefix=$PREFIX
+make LDFLAGS=-lintl
+make install
+
+cd $TMP/gcc-3.3.6
+mkdir i386
+cd i386
+../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
+make
+make install
+
+cd $TMP/gdb-6.3
+mkdir i386
+cd i386
+../configure --target=i386-elf --prefix=$PREFIX --disable-tui
+make LDFLAGS=-lintl
+make install
diff --git a/src/misc/gdb-macros b/src/misc/gdb-macros
new file mode 100644
index 0000000..3babb52
--- /dev/null
+++ b/src/misc/gdb-macros
@@ -0,0 +1,140 @@
+#
+# A set of useful macros that can help debug Pintos.
+#
+# Include with "source" cmd in gdb.
+# Use "help user-defined" for help.
+#
+# Author: Godmar Back <gback@cs.vt.edu>, Feb 2006
+#
+# $Id: gdb-macros,v 1.1 2006-04-07 18:29:34 blp Exp $
+#
+
+# for internal use
+define offsetof
+    set $rc = (char*)&((struct $arg0 *)0)->$arg1 - (char*)0
+end
+
+define list_entry
+    offsetof $arg1 $arg2
+    set $rc = ((struct $arg1 *) ((uint8_t *) ($arg0) - $rc))
+end
+
+# dump a Pintos list
+define dumplist
+    set $list = $arg0
+    set $e = $list->head.next
+    set $i = 0
+    while $e != &(($arg0).tail)
+        list_entry $e $arg1 $arg2
+        set $l = $rc
+        printf "pintos-debug: dumplist #%d: %p ", $i++, $l
+        output *$l
+        set $e = $e->next
+        printf "\n"
+    end
+end
+
+document dumplist
+    Dump the content of a Pintos list, 
+    invoke as dumplist name_of_list name_of_struct name_of_elem_in_list_struct
+end
+
+# print a thread's backtrace, given a pointer to the struct thread *
+define btthread
+   if $arg0 == ($esp - ((unsigned)$esp % 4096)) 
+	bt
+   else
+       set $saveEIP = $eip 
+       set $saveESP = $esp 
+       set $saveEBP = $ebp 
+
+       set $esp = ((struct thread *)$arg0)->stack
+       set $ebp = ((void**)$esp)[2]
+       set $eip = ((void**)$esp)[4]
+
+       bt
+
+       set $eip = $saveEIP
+       set $esp = $saveESP
+       set $ebp = $saveEBP
+   end
+end
+document btthread
+    Show the backtrace of a thread,
+    invoke as btthread pointer_to_struct_thread
+end
+
+# print backtraces associated with all threads in a list
+define btthreadlist
+    set $list = $arg0
+    set $e = $list->head.next
+    while $e != &(($arg0).tail)
+        list_entry $e thread $arg1
+        printf "pintos-debug: dumping backtrace of thread '%s' @%p\n", \
+                ((struct thread*)$rc)->name, $rc
+        btthread $rc
+        set $e = $e->next
+        printf "\n"
+    end
+end
+document btthreadlist
+    Given a list of threads, print each thread's backtrace
+    invoke as btthreadlist name_of_list name_of_elem_in_list_struct
+end
+
+# print backtraces of all threads (based on 'all_list' all threads list)
+define btthreadall
+    btthreadlist &all_list allelem
+end
+document btthreadall
+    Print backtraces of all threads
+end
+
+# print a correct backtrace by adjusting $eip
+# this works best right at intr0e_stub
+define btpagefault
+    set $saveeip = $eip
+    set $eip = ((void**)$esp)[1]
+    backtrace
+    set $eip = $saveeip
+end
+document btpagefault
+    Print a backtrace of the current thread after a pagefault
+end
+
+# invoked whenever the program stops
+define hook-stop
+    # stopped at stub #0E = #14 (page fault exception handler stub)
+    if ($eip == intr0e_stub)
+        set $savedeip = ((void**)$esp)[1]
+        # if this was in user mode, the OS should handle it
+        # either handle the page fault or terminate the process
+        if ($savedeip < 0xC0000000)
+            printf "pintos-debug: a page fault exception occurred in user mode\n"
+            printf "pintos-debug: hit 'c' to continue, or 's' to step to intr_handler\n"
+        else
+            # if this was in kernel mode, a stack trace might be useful
+            printf "pintos-debug: a page fault occurred in kernel mode\n"
+            btpagefault
+        end
+    end
+end
+
+# load symbols for a Pintos user program
+define loadusersymbols
+    shell objdump -h $arg0 | awk '/.text/ { print "add-symbol-file $arg0 0x"$4 }' > .loadsymbols
+    source .loadsymbols
+    shell rm -f .loadsymbols
+end
+document loadusersymbols
+    Load the symbols contained in a user program's executable.
+    Example:
+        loadusersymbols tests/userprog/exec-multiple
+end
+
+define debugpintos
+    target remote localhost:1234
+end
+document debugpintos
+    Attach debugger to pintos process
+end
diff --git a/src/tests/Algorithm/Diff.pm b/src/tests/Algorithm/Diff.pm
new file mode 100644
index 0000000..904c530
--- /dev/null
+++ b/src/tests/Algorithm/Diff.pm
@@ -0,0 +1,1713 @@
+package Algorithm::Diff;
+# Skip to first "=head" line for documentation.
+use strict;
+
+use integer;    # see below in _replaceNextLargerWith() for mod to make
+                # if you don't use this
+use vars qw( $VERSION @EXPORT_OK );
+$VERSION = 1.19_01;
+#          ^ ^^ ^^-- Incremented at will
+#          | \+----- Incremented for non-trivial changes to features
+#          \-------- Incremented for fundamental changes
+require Exporter;
+*import    = \&Exporter::import;
+@EXPORT_OK = qw(
+    prepare LCS LCDidx LCS_length
+    diff sdiff compact_diff
+    traverse_sequences traverse_balanced
+);
+
+# McIlroy-Hunt diff algorithm
+# Adapted from the Smalltalk code of Mario I. Wolczko, <mario@wolczko.com>
+# by Ned Konz, perl@bike-nomad.com
+# Updates by Tye McQueen, http://perlmonks.org/?node=tye
+
+# Create a hash that maps each element of $aCollection to the set of
+# positions it occupies in $aCollection, restricted to the elements
+# within the range of indexes specified by $start and $end.
+# The fourth parameter is a subroutine reference that will be called to
+# generate a string to use as a key.
+# Additional parameters, if any, will be passed to this subroutine.
+#
+# my $hashRef = _withPositionsOfInInterval( \@array, $start, $end, $keyGen );
+
+sub _withPositionsOfInInterval
+{
+    my $aCollection = shift;    # array ref
+    my $start       = shift;
+    my $end         = shift;
+    my $keyGen      = shift;
+    my %d;
+    my $index;
+    for ( $index = $start ; $index <= $end ; $index++ )
+    {
+        my $element = $aCollection->[$index];
+        my $key = &$keyGen( $element, @_ );
+        if ( exists( $d{$key} ) )
+        {
+            unshift ( @{ $d{$key} }, $index );
+        }
+        else
+        {
+            $d{$key} = [$index];
+        }
+    }
+    return wantarray ? %d : \%d;
+}
+
+# Find the place at which aValue would normally be inserted into the
+# array. If that place is already occupied by aValue, do nothing, and
+# return undef. If the place does not exist (i.e., it is off the end of
+# the array), add it to the end, otherwise replace the element at that
+# point with aValue.  It is assumed that the array's values are numeric.
+# This is where the bulk (75%) of the time is spent in this module, so
+# try to make it fast!
+
+sub _replaceNextLargerWith
+{
+    my ( $array, $aValue, $high ) = @_;
+    $high ||= $#$array;
+
+    # off the end?
+    if ( $high == -1 || $aValue > $array->[-1] )
+    {
+        push ( @$array, $aValue );
+        return $high + 1;
+    }
+
+    # binary search for insertion point...
+    my $low = 0;
+    my $index;
+    my $found;
+    while ( $low <= $high )
+    {
+        $index = ( $high + $low ) / 2;
+
+        # $index = int(( $high + $low ) / 2);  # without 'use integer'
+        $found = $array->[$index];
+
+        if ( $aValue == $found )
+        {
+            return undef;
+        }
+        elsif ( $aValue > $found )
+        {
+            $low = $index + 1;
+        }
+        else
+        {
+            $high = $index - 1;
+        }
+    }
+
+    # now insertion point is in $low.
+    $array->[$low] = $aValue;    # overwrite next larger
+    return $low;
+}
+
+# This method computes the longest common subsequence in $a and $b.
+
+# Result is array or ref, whose contents is such that
+#   $a->[ $i ] == $b->[ $result[ $i ] ]
+# foreach $i in ( 0 .. $#result ) if $result[ $i ] is defined.
+
+# An additional argument may be passed; this is a hash or key generating
+# function that should return a string that uniquely identifies the given
+# element.  It should be the case that if the key is the same, the elements
+# will compare the same. If this parameter is undef or missing, the key
+# will be the element as a string.
+
+# By default, comparisons will use "eq" and elements will be turned into keys
+# using the default stringizing operator '""'.
+
+# Additional parameters, if any, will be passed to the key generation
+# routine.
+
+sub _longestCommonSubsequence
+{
+    my $a        = shift;    # array ref or hash ref
+    my $b        = shift;    # array ref or hash ref
+    my $counting = shift;    # scalar
+    my $keyGen   = shift;    # code ref
+    my $compare;             # code ref
+
+    if ( ref($a) eq 'HASH' )
+    {                        # prepared hash must be in $b
+        my $tmp = $b;
+        $b = $a;
+        $a = $tmp;
+    }
+
+    # Check for bogus (non-ref) argument values
+    if ( !ref($a) || !ref($b) )
+    {
+        my @callerInfo = caller(1);
+        die 'error: must pass array or hash references to ' . $callerInfo[3];
+    }
+
+    # set up code refs
+    # Note that these are optimized.
+    if ( !defined($keyGen) )    # optimize for strings
+    {
+        $keyGen = sub { $_[0] };
+        $compare = sub { my ( $a, $b ) = @_; $a eq $b };
+    }
+    else
+    {
+        $compare = sub {
+            my $a = shift;
+            my $b = shift;
+            &$keyGen( $a, @_ ) eq &$keyGen( $b, @_ );
+        };
+    }
+
+    my ( $aStart, $aFinish, $matchVector ) = ( 0, $#$a, [] );
+    my ( $prunedCount, $bMatches ) = ( 0, {} );
+
+    if ( ref($b) eq 'HASH' )    # was $bMatches prepared for us?
+    {
+        $bMatches = $b;
+    }
+    else
+    {
+        my ( $bStart, $bFinish ) = ( 0, $#$b );
+
+        # First we prune off any common elements at the beginning
+        while ( $aStart <= $aFinish
+            and $bStart <= $bFinish
+            and &$compare( $a->[$aStart], $b->[$bStart], @_ ) )
+        {
+            $matchVector->[ $aStart++ ] = $bStart++;
+            $prunedCount++;
+        }
+
+        # now the end
+        while ( $aStart <= $aFinish
+            and $bStart <= $bFinish
+            and &$compare( $a->[$aFinish], $b->[$bFinish], @_ ) )
+        {
+            $matchVector->[ $aFinish-- ] = $bFinish--;
+            $prunedCount++;
+        }
+
+        # Now compute the equivalence classes of positions of elements
+        $bMatches =
+          _withPositionsOfInInterval( $b, $bStart, $bFinish, $keyGen, @_ );
+    }
+    my $thresh = [];
+    my $links  = [];
+
+    my ( $i, $ai, $j, $k );
+    for ( $i = $aStart ; $i <= $aFinish ; $i++ )
+    {
+        $ai = &$keyGen( $a->[$i], @_ );
+        if ( exists( $bMatches->{$ai} ) )
+        {
+            $k = 0;
+            for $j ( @{ $bMatches->{$ai} } )
+            {
+
+                # optimization: most of the time this will be true
+                if ( $k and $thresh->[$k] > $j and $thresh->[ $k - 1 ] < $j )
+                {
+                    $thresh->[$k] = $j;
+                }
+                else
+                {
+                    $k = _replaceNextLargerWith( $thresh, $j, $k );
+                }
+
+                # oddly, it's faster to always test this (CPU cache?).
+                if ( defined($k) )
+                {
+                    $links->[$k] =
+                      [ ( $k ? $links->[ $k - 1 ] : undef ), $i, $j ];
+                }
+            }
+        }
+    }
+
+    if (@$thresh)
+    {
+        return $prunedCount + @$thresh if $counting;
+        for ( my $link = $links->[$#$thresh] ; $link ; $link = $link->[0] )
+        {
+            $matchVector->[ $link->[1] ] = $link->[2];
+        }
+    }
+    elsif ($counting)
+    {
+        return $prunedCount;
+    }
+
+    return wantarray ? @$matchVector : $matchVector;
+}
+
+sub traverse_sequences
+{
+    my $a                 = shift;          # array ref
+    my $b                 = shift;          # array ref
+    my $callbacks         = shift || {};
+    my $keyGen            = shift;
+    my $matchCallback     = $callbacks->{'MATCH'} || sub { };
+    my $discardACallback  = $callbacks->{'DISCARD_A'} || sub { };
+    my $finishedACallback = $callbacks->{'A_FINISHED'};
+    my $discardBCallback  = $callbacks->{'DISCARD_B'} || sub { };
+    my $finishedBCallback = $callbacks->{'B_FINISHED'};
+    my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ );
+
+    # Process all the lines in @$matchVector
+    my $lastA = $#$a;
+    my $lastB = $#$b;
+    my $bi    = 0;
+    my $ai;
+
+    for ( $ai = 0 ; $ai <= $#$matchVector ; $ai++ )
+    {
+        my $bLine = $matchVector->[$ai];
+        if ( defined($bLine) )    # matched
+        {
+            &$discardBCallback( $ai, $bi++, @_ ) while $bi < $bLine;
+            &$matchCallback( $ai,    $bi++, @_ );
+        }
+        else
+        {
+            &$discardACallback( $ai, $bi, @_ );
+        }
+    }
+
+    # The last entry (if any) processed was a match.
+    # $ai and $bi point just past the last matching lines in their sequences.
+
+    while ( $ai <= $lastA or $bi <= $lastB )
+    {
+
+        # last A?
+        if ( $ai == $lastA + 1 and $bi <= $lastB )
+        {
+            if ( defined($finishedACallback) )
+            {
+                &$finishedACallback( $lastA, @_ );
+                $finishedACallback = undef;
+            }
+            else
+            {
+                &$discardBCallback( $ai, $bi++, @_ ) while $bi <= $lastB;
+            }
+        }
+
+        # last B?
+        if ( $bi == $lastB + 1 and $ai <= $lastA )
+        {
+            if ( defined($finishedBCallback) )
+            {
+                &$finishedBCallback( $lastB, @_ );
+                $finishedBCallback = undef;
+            }
+            else
+            {
+                &$discardACallback( $ai++, $bi, @_ ) while $ai <= $lastA;
+            }
+        }
+
+        &$discardACallback( $ai++, $bi, @_ ) if $ai <= $lastA;
+        &$discardBCallback( $ai, $bi++, @_ ) if $bi <= $lastB;
+    }
+
+    return 1;
+}
+
+sub traverse_balanced
+{
+    my $a                 = shift;              # array ref
+    my $b                 = shift;              # array ref
+    my $callbacks         = shift || {};
+    my $keyGen            = shift;
+    my $matchCallback     = $callbacks->{'MATCH'} || sub { };
+    my $discardACallback  = $callbacks->{'DISCARD_A'} || sub { };
+    my $discardBCallback  = $callbacks->{'DISCARD_B'} || sub { };
+    my $changeCallback    = $callbacks->{'CHANGE'};
+    my $matchVector = _longestCommonSubsequence( $a, $b, 0, $keyGen, @_ );
+
+    # Process all the lines in match vector
+    my $lastA = $#$a;
+    my $lastB = $#$b;
+    my $bi    = 0;
+    my $ai    = 0;
+    my $ma    = -1;
+    my $mb;
+
+    while (1)
+    {
+
+        # Find next match indices $ma and $mb
+        do {
+            $ma++;
+        } while(
+                $ma <= $#$matchVector
+            &&  !defined $matchVector->[$ma]
+        );
+
+        last if $ma > $#$matchVector;    # end of matchVector?
+        $mb = $matchVector->[$ma];
+
+        # Proceed with discard a/b or change events until
+        # next match
+        while ( $ai < $ma || $bi < $mb )
+        {
+
+            if ( $ai < $ma && $bi < $mb )
+            {
+
+                # Change
+                if ( defined $changeCallback )
+                {
+                    &$changeCallback( $ai++, $bi++, @_ );
+                }
+                else
+                {
+                    &$discardACallback( $ai++, $bi, @_ );
+                    &$discardBCallback( $ai, $bi++, @_ );
+                }
+            }
+            elsif ( $ai < $ma )
+            {
+                &$discardACallback( $ai++, $bi, @_ );
+            }
+            else
+            {
+
+                # $bi < $mb
+                &$discardBCallback( $ai, $bi++, @_ );
+            }
+        }
+
+        # Match
+        &$matchCallback( $ai++, $bi++, @_ );
+    }
+
+    while ( $ai <= $lastA || $bi <= $lastB )
+    {
+        if ( $ai <= $lastA && $bi <= $lastB )
+        {
+
+            # Change
+            if ( defined $changeCallback )
+            {
+                &$changeCallback( $ai++, $bi++, @_ );
+            }
+            else
+            {
+                &$discardACallback( $ai++, $bi, @_ );
+                &$discardBCallback( $ai, $bi++, @_ );
+            }
+        }
+        elsif ( $ai <= $lastA )
+        {
+            &$discardACallback( $ai++, $bi, @_ );
+        }
+        else
+        {
+
+            # $bi <= $lastB
+            &$discardBCallback( $ai, $bi++, @_ );
+        }
+    }
+
+    return 1;
+}
+
+sub prepare
+{
+    my $a       = shift;    # array ref
+    my $keyGen  = shift;    # code ref
+
+    # set up code ref
+    $keyGen = sub { $_[0] } unless defined($keyGen);
+
+    return scalar _withPositionsOfInInterval( $a, 0, $#$a, $keyGen, @_ );
+}
+
+sub LCS
+{
+    my $a = shift;                  # array ref
+    my $b = shift;                  # array ref or hash ref
+    my $matchVector = _longestCommonSubsequence( $a, $b, 0, @_ );
+    my @retval;
+    my $i;
+    for ( $i = 0 ; $i <= $#$matchVector ; $i++ )
+    {
+        if ( defined( $matchVector->[$i] ) )
+        {
+            push ( @retval, $a->[$i] );
+        }
+    }
+    return wantarray ? @retval : \@retval;
+}
+
+sub LCS_length
+{
+    my $a = shift;                          # array ref
+    my $b = shift;                          # array ref or hash ref
+    return _longestCommonSubsequence( $a, $b, 1, @_ );
+}
+
+sub LCSidx
+{
+    my $a= shift @_;
+    my $b= shift @_;
+    my $match= _longestCommonSubsequence( $a, $b, 0, @_ );
+    my @am= grep defined $match->[$_], 0..$#$match;
+    my @bm= @{$match}[@am];
+    return \@am, \@bm;
+}
+
+sub compact_diff
+{
+    my $a= shift @_;
+    my $b= shift @_;
+    my( $am, $bm )= LCSidx( $a, $b, @_ );
+    my @cdiff;
+    my( $ai, $bi )= ( 0, 0 );
+    push @cdiff, $ai, $bi;
+    while( 1 ) {
+        while(  @$am  &&  $ai == $am->[0]  &&  $bi == $bm->[0]  ) {
+            shift @$am;
+            shift @$bm;
+            ++$ai, ++$bi;
+        }
+        push @cdiff, $ai, $bi;
+        last   if  ! @$am;
+        $ai = $am->[0];
+        $bi = $bm->[0];
+        push @cdiff, $ai, $bi;
+    }
+    push @cdiff, 0+@$a, 0+@$b
+        if  $ai < @$a || $bi < @$b;
+    return wantarray ? @cdiff : \@cdiff;
+}
+
+sub diff
+{
+    my $a      = shift;    # array ref
+    my $b      = shift;    # array ref
+    my $retval = [];
+    my $hunk   = [];
+    my $discard = sub {
+        push @$hunk, [ '-', $_[0], $a->[ $_[0] ] ];
+    };
+    my $add = sub {
+        push @$hunk, [ '+', $_[1], $b->[ $_[1] ] ];
+    };
+    my $match = sub {
+        push @$retval, $hunk
+            if 0 < @$hunk;
+        $hunk = []
+    };
+    traverse_sequences( $a, $b,
+        { MATCH => $match, DISCARD_A => $discard, DISCARD_B => $add }, @_ );
+    &$match();
+    return wantarray ? @$retval : $retval;
+}
+
+sub sdiff
+{
+    my $a      = shift;    # array ref
+    my $b      = shift;    # array ref
+    my $retval = [];
+    my $discard = sub { push ( @$retval, [ '-', $a->[ $_[0] ], "" ] ) };
+    my $add = sub { push ( @$retval, [ '+', "", $b->[ $_[1] ] ] ) };
+    my $change = sub {
+        push ( @$retval, [ 'c', $a->[ $_[0] ], $b->[ $_[1] ] ] );
+    };
+    my $match = sub {
+        push ( @$retval, [ 'u', $a->[ $_[0] ], $b->[ $_[1] ] ] );
+    };
+    traverse_balanced(
+        $a,
+        $b,
+        {
+            MATCH     => $match,
+            DISCARD_A => $discard,
+            DISCARD_B => $add,
+            CHANGE    => $change,
+        },
+        @_
+    );
+    return wantarray ? @$retval : $retval;
+}
+
+########################################
+my $Root= __PACKAGE__;
+package Algorithm::Diff::_impl;
+use strict;
+
+sub _Idx()  { 0 } # $me->[_Idx]: Ref to array of hunk indices
+            # 1   # $me->[1]: Ref to first sequence
+            # 2   # $me->[2]: Ref to second sequence
+sub _End()  { 3 } # $me->[_End]: Diff between forward and reverse pos
+sub _Same() { 4 } # $me->[_Same]: 1 if pos 1 contains unchanged items
+sub _Base() { 5 } # $me->[_Base]: Added to range's min and max
+sub _Pos()  { 6 } # $me->[_Pos]: Which hunk is currently selected
+sub _Off()  { 7 } # $me->[_Off]: Offset into _Idx for current position
+sub _Min() { -2 } # Added to _Off to get min instead of max+1
+
+sub Die
+{
+    require Carp;
+    Carp::confess( @_ );
+}
+
+sub _ChkPos
+{
+    my( $me )= @_;
+    return   if  $me->[_Pos];
+    my $meth= ( caller(1) )[3];
+    Die( "Called $meth on 'reset' object" );
+}
+
+sub _ChkSeq
+{
+    my( $me, $seq )= @_;
+    return $seq + $me->[_Off]
+        if  1 == $seq  ||  2 == $seq;
+    my $meth= ( caller(1) )[3];
+    Die( "$meth: Invalid sequence number ($seq); must be 1 or 2" );
+}
+
+sub getObjPkg
+{
+    my( $us )= @_;
+    return ref $us   if  ref $us;
+    return $us . "::_obj";
+}
+
+sub new
+{
+    my( $us, $seq1, $seq2, $opts ) = @_;
+    my @args;
+    for( $opts->{keyGen} ) {
+        push @args, $_   if  $_;
+    }
+    for( $opts->{keyGenArgs} ) {
+        push @args, @$_   if  $_;
+    }
+    my $cdif= Algorithm::Diff::compact_diff( $seq1, $seq2, @args );
+    my $same= 1;
+    if(  0 == $cdif->[2]  &&  0 == $cdif->[3]  ) {
+        $same= 0;
+        splice @$cdif, 0, 2;
+    }
+    my @obj= ( $cdif, $seq1, $seq2 );
+    $obj[_End] = (1+@$cdif)/2;
+    $obj[_Same] = $same;
+    $obj[_Base] = 0;
+    my $me = bless \@obj, $us->getObjPkg();
+    $me->Reset( 0 );
+    return $me;
+}
+
+sub Reset
+{
+    my( $me, $pos )= @_;
+    $pos= int( $pos || 0 );
+    $pos += $me->[_End]
+        if  $pos < 0;
+    $pos= 0
+        if  $pos < 0  ||  $me->[_End] <= $pos;
+    $me->[_Pos]= $pos || !1;
+    $me->[_Off]= 2*$pos - 1;
+    return $me;
+}
+
+sub Base
+{
+    my( $me, $base )= @_;
+    my $oldBase= $me->[_Base];
+    $me->[_Base]= 0+$base   if  defined $base;
+    return $oldBase;
+}
+
+sub Copy
+{
+    my( $me, $pos, $base )= @_;
+    my @obj= @$me;
+    my $you= bless \@obj, ref($me);
+    $you->Reset( $pos )   if  defined $pos;
+    $you->Base( $base );
+    return $you;
+}
+
+sub Next {
+    my( $me, $steps )= @_;
+    $steps= 1   if  ! defined $steps;
+    if( $steps ) {
+        my $pos= $me->[_Pos];
+        my $new= $pos + $steps;
+        $new= 0   if  $pos  &&  $new < 0;
+        $me->Reset( $new )
+    }
+    return $me->[_Pos];
+}
+
+sub Prev {
+    my( $me, $steps )= @_;
+    $steps= 1   if  ! defined $steps;
+    my $pos= $me->Next(-$steps);
+    $pos -= $me->[_End]   if  $pos;
+    return $pos;
+}
+
+sub Diff {
+    my( $me )= @_;
+    $me->_ChkPos();
+    return 0   if  $me->[_Same] == ( 1 & $me->[_Pos] );
+    my $ret= 0;
+    my $off= $me->[_Off];
+    for my $seq ( 1, 2 ) {
+        $ret |= $seq
+            if  $me->[_Idx][ $off + $seq + _Min ]
+            <   $me->[_Idx][ $off + $seq ];
+    }
+    return $ret;
+}
+
+sub Min {
+    my( $me, $seq, $base )= @_;
+    $me->_ChkPos();
+    my $off= $me->_ChkSeq($seq);
+    $base= $me->[_Base] if !defined $base;
+    return $base + $me->[_Idx][ $off + _Min ];
+}
+
+sub Max {
+    my( $me, $seq, $base )= @_;
+    $me->_ChkPos();
+    my $off= $me->_ChkSeq($seq);
+    $base= $me->[_Base] if !defined $base;
+    return $base + $me->[_Idx][ $off ] -1;
+}
+
+sub Range {
+    my( $me, $seq, $base )= @_;
+    $me->_ChkPos();
+    my $off = $me->_ChkSeq($seq);
+    if( !wantarray ) {
+        return  $me->[_Idx][ $off ]
+            -   $me->[_Idx][ $off + _Min ];
+    }
+    $base= $me->[_Base] if !defined $base;
+    return  ( $base + $me->[_Idx][ $off + _Min ] )
+        ..  ( $base + $me->[_Idx][ $off ] - 1 );
+}
+
+sub Items {
+    my( $me, $seq )= @_;
+    $me->_ChkPos();
+    my $off = $me->_ChkSeq($seq);
+    if( !wantarray ) {
+        return  $me->[_Idx][ $off ]
+            -   $me->[_Idx][ $off + _Min ];
+    }
+    return
+        @{$me->[$seq]}[
+                $me->[_Idx][ $off + _Min ]
+            ..  ( $me->[_Idx][ $off ] - 1 )
+        ];
+}
+
+sub Same {
+    my( $me )= @_;
+    $me->_ChkPos();
+    return wantarray ? () : 0
+        if  $me->[_Same] != ( 1 & $me->[_Pos] );
+    return $me->Items(1);
+}
+
+my %getName;
+BEGIN {
+    %getName= (
+        same => \&Same,
+        diff => \&Diff,
+        base => \&Base,
+        min  => \&Min,
+        max  => \&Max,
+        range=> \&Range,
+        items=> \&Items, # same thing
+    );
+}
+
+sub Get
+{
+    my $me= shift @_;
+    $me->_ChkPos();
+    my @value;
+    for my $arg (  @_  ) {
+        for my $word (  split ' ', $arg  ) {
+            my $meth;
+            if(     $word !~ /^(-?\d+)?([a-zA-Z]+)([12])?$/
+                ||  not  $meth= $getName{ lc $2 }
+            ) {
+                Die( $Root, ", Get: Invalid request ($word)" );
+            }
+            my( $base, $name, $seq )= ( $1, $2, $3 );
+            push @value, scalar(
+                4 == length($name)
+                    ? $meth->( $me )
+                    : $meth->( $me, $seq, $base )
+            );
+        }
+    }
+    if(  wantarray  ) {
+        return @value;
+    } elsif(  1 == @value  ) {
+        return $value[0];
+    }
+    Die( 0+@value, " values requested from ",
+        $Root, "'s Get in scalar context" );
+}
+
+
+my $Obj= getObjPkg($Root);
+no strict 'refs';
+
+for my $meth (  qw( new getObjPkg )  ) {
+    *{$Root."::".$meth} = \&{$meth};
+    *{$Obj ."::".$meth} = \&{$meth};
+}
+for my $meth (  qw(
+    Next Prev Reset Copy Base Diff
+    Same Items Range Min Max Get
+    _ChkPos _ChkSeq
+)  ) {
+    *{$Obj."::".$meth} = \&{$meth};
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Algorithm::Diff - Compute `intelligent' differences between two files / lists
+
+=head1 SYNOPSIS
+
+    require Algorithm::Diff;
+
+    # This example produces traditional 'diff' output:
+
+    my $diff = Algorithm::Diff->new( \@seq1, \@seq2 );
+
+    $diff->Base( 1 );   # Return line numbers, not indices
+    while(  $diff->Next()  ) {
+        next   if  $diff->Same();
+        my $sep = '';
+        if(  ! $diff->Items(2)  ) {
+            sprintf "%d,%dd%d\n",
+                $diff->Get(qw( Min1 Max1 Max2 ));
+        } elsif(  ! $diff->Items(1)  ) {
+            sprint "%da%d,%d\n",
+                $diff->Get(qw( Max1 Min2 Max2 ));
+        } else {
+            $sep = "---\n";
+            sprintf "%d,%dc%d,%d\n",
+                $diff->Get(qw( Min1 Max1 Min2 Max2 ));
+        }
+        print "< $_"   for  $diff->Items(1);
+        print $sep;
+        print "> $_"   for  $diff->Items(2);
+    }
+
+
+    # Alternate interfaces:
+
+    use Algorithm::Diff qw(
+        LCS LCS_length LCSidx
+        diff sdiff compact_diff
+        traverse_sequences traverse_balanced );
+
+    @lcs    = LCS( \@seq1, \@seq2 );
+    $lcsref = LCS( \@seq1, \@seq2 );
+    $count  = LCS_length( \@seq1, \@seq2 );
+
+    ( $seq1idxref, $seq2idxref ) = LCSidx( \@seq1, \@seq2 );
+
+
+    # Complicated interfaces:
+
+    @diffs  = diff( \@seq1, \@seq2 );
+
+    @sdiffs = sdiff( \@seq1, \@seq2 );
+
+    @cdiffs = compact_diff( \@seq1, \@seq2 );
+
+    traverse_sequences(
+        \@seq1,
+        \@seq2,
+        {   MATCH     => \&callback1,
+            DISCARD_A => \&callback2,
+            DISCARD_B => \&callback3,
+        },
+        \&key_generator,
+        @extra_args,
+    );
+
+    traverse_balanced(
+        \@seq1,
+        \@seq2,
+        {   MATCH     => \&callback1,
+            DISCARD_A => \&callback2,
+            DISCARD_B => \&callback3,
+            CHANGE    => \&callback4,
+        },
+        \&key_generator,
+        @extra_args,
+    );
+
+
+=head1 INTRODUCTION
+
+(by Mark-Jason Dominus)
+
+I once read an article written by the authors of C<diff>; they said
+that they worked very hard on the algorithm until they found the
+right one.
+
+I think what they ended up using (and I hope someone will correct me,
+because I am not very confident about this) was the `longest common
+subsequence' method.  In the LCS problem, you have two sequences of
+items:
+
+    a b c d f g h j q z
+
+    a b c d e f g i j k r x y z
+
+and you want to find the longest sequence of items that is present in
+both original sequences in the same order.  That is, you want to find
+a new sequence I<S> which can be obtained from the first sequence by
+deleting some items, and from the secend sequence by deleting other
+items.  You also want I<S> to be as long as possible.  In this case I<S>
+is
+
+    a b c d f g j z
+
+From there it's only a small step to get diff-like output:
+
+    e   h i   k   q r x y
+    +   - +   +   - + + +
+
+This module solves the LCS problem.  It also includes a canned function
+to generate C<diff>-like output.
+
+It might seem from the example above that the LCS of two sequences is
+always pretty obvious, but that's not always the case, especially when
+the two sequences have many repeated elements.  For example, consider
+
+    a x b y c z p d q
+    a b c a x b y c z
+
+A naive approach might start by matching up the C<a> and C<b> that
+appear at the beginning of each sequence, like this:
+
+    a x b y c         z p d q
+    a   b   c a b y c z
+
+This finds the common subsequence C<a b c z>.  But actually, the LCS
+is C<a x b y c z>:
+
+          a x b y c z p d q
+    a b c a x b y c z
+
+or
+
+    a       x b y c z p d q
+    a b c a x b y c z
+
+=head1 USAGE
+
+(See also the README file and several example
+scripts include with this module.)
+
+This module now provides an object-oriented interface that uses less
+memory and is easier to use than most of the previous procedural
+interfaces.  It also still provides several exportable functions.  We'll
+deal with these in ascending order of difficulty:  C<LCS>,
+C<LCS_length>, C<LCSidx>, OO interface, C<prepare>, C<diff>, C<sdiff>,
+C<traverse_sequences>, and C<traverse_balanced>.
+
+=head2 C<LCS>
+
+Given references to two lists of items, LCS returns an array containing
+their longest common subsequence.  In scalar context, it returns a
+reference to such a list.
+
+    @lcs    = LCS( \@seq1, \@seq2 );
+    $lcsref = LCS( \@seq1, \@seq2 );
+
+C<LCS> may be passed an optional third parameter; this is a CODE
+reference to a key generation function.  See L</KEY GENERATION
+FUNCTIONS>.
+
+    @lcs    = LCS( \@seq1, \@seq2, \&keyGen, @args );
+    $lcsref = LCS( \@seq1, \@seq2, \&keyGen, @args );
+
+Additional parameters, if any, will be passed to the key generation
+routine.
+
+=head2 C<LCS_length>
+
+This is just like C<LCS> except it only returns the length of the
+longest common subsequence.  This provides a performance gain of about
+9% compared to C<LCS>.
+
+=head2 C<LCSidx>
+
+Like C<LCS> except it returns references to two arrays.  The first array
+contains the indices into @seq1 where the LCS items are located.  The
+second array contains the indices into @seq2 where the LCS items are located.
+
+Therefore, the following three lists will contain the same values:
+
+    my( $idx1, $idx2 ) = LCSidx( \@seq1, \@seq2 );
+    my @list1 = @seq1[ @$idx1 ];
+    my @list2 = @seq2[ @$idx2 ];
+    my @list3 = LCS( \@seq1, \@seq2 );
+
+=head2 C<new>
+
+    $diff = Algorithm::Diffs->new( \@seq1, \@seq2 );
+    $diff = Algorithm::Diffs->new( \@seq1, \@seq2, \%opts );
+
+C<new> computes the smallest set of additions and deletions necessary
+to turn the first sequence into the second and compactly records them
+in the object.
+
+You use the object to iterate over I<hunks>, where each hunk represents
+a contiguous section of items which should be added, deleted, replaced,
+or left unchanged.
+
+=over 4
+
+The following summary of all of the methods looks a lot like Perl code
+but some of the symbols have different meanings:
+
+    [ ]     Encloses optional arguments
+    :       Is followed by the default value for an optional argument
+    |       Separates alternate return results
+
+Method summary:
+
+    $obj        = Algorithm::Diff->new( \@seq1, \@seq2, [ \%opts ] );
+    $pos        = $obj->Next(  [ $count : 1 ] );
+    $revPos     = $obj->Prev(  [ $count : 1 ] );
+    $obj        = $obj->Reset( [ $pos : 0 ] );
+    $copy       = $obj->Copy(  [ $pos, [ $newBase ] ] );
+    $oldBase    = $obj->Base(  [ $newBase ] );
+
+Note that all of the following methods C<die> if used on an object that
+is "reset" (not currently pointing at any hunk).
+
+    $bits       = $obj->Diff(  );
+    @items|$cnt = $obj->Same(  );
+    @items|$cnt = $obj->Items( $seqNum );
+    @idxs |$cnt = $obj->Range( $seqNum, [ $base ] );
+    $minIdx     = $obj->Min(   $seqNum, [ $base ] );
+    $maxIdx     = $obj->Max(   $seqNum, [ $base ] );
+    @values     = $obj->Get(   @names );
+
+Passing in C<undef> for an optional argument is always treated the same
+as if no argument were passed in.
+
+=item C<Next>
+
+    $pos = $diff->Next();    # Move forward 1 hunk
+    $pos = $diff->Next( 2 ); # Move forward 2 hunks
+    $pos = $diff->Next(-5);  # Move backward 5 hunks
+
+C<Next> moves the object to point at the next hunk.  The object starts
+out "reset", which means it isn't pointing at any hunk.  If the object
+is reset, then C<Next()> moves to the first hunk.
+
+C<Next> returns a true value iff the move didn't go past the last hunk.
+So C<Next(0)> will return true iff the object is not reset.
+
+Actually, C<Next> returns the object's new position, which is a number
+between 1 and the number of hunks (inclusive), or returns a false value.
+
+=item C<Prev>
+
+C<Prev($N)> is almost identical to C<Next(-$N)>; it moves to the $Nth
+previous hunk.  On a 'reset' object, C<Prev()> [and C<Next(-1)>] move
+to the last hunk.
+
+The position returned by C<Prev> is relative to the I<end> of the
+hunks; -1 for the last hunk, -2 for the second-to-last, etc.
+
+=item C<Reset>
+
+    $diff->Reset();     # Reset the object's position
+    $diff->Reset($pos); # Move to the specified hunk
+    $diff->Reset(1);    # Move to the first hunk
+    $diff->Reset(-1);   # Move to the last hunk
+
+C<Reset> returns the object, so, for example, you could use
+C<< $diff->Reset()->Next(-1) >> to get the number of hunks.
+
+=item C<Copy>
+
+    $copy = $diff->Copy( $newPos, $newBase );
+
+C<Copy> returns a copy of the object.  The copy and the orignal object
+share most of their data, so making copies takes very little memory.
+The copy maintains its own position (separate from the original), which
+is the main purpose of copies.  It also maintains its own base.
+
+By default, the copy's position starts out the same as the original
+object's position.  But C<Copy> takes an optional first argument to set the
+new position, so the following three snippets are equivalent:
+
+    $copy = $diff->Copy($pos);
+
+    $copy = $diff->Copy();
+    $copy->Reset($pos);
+
+    $copy = $diff->Copy()->Reset($pos);
+
+C<Copy> takes an optional second argument to set the base for
+the copy.  If you wish to change the base of the copy but leave
+the position the same as in the original, here are two
+equivalent ways:
+
+    $copy = $diff->Copy();
+    $copy->Base( 0 );
+
+    $copy = $diff->Copy(undef,0);
+
+Here are two equivalent way to get a "reset" copy:
+
+    $copy = $diff->Copy(0);
+
+    $copy = $diff->Copy()->Reset();
+
+=item C<Diff>
+
+    $bits = $obj->Diff();
+
+C<Diff> returns a true value iff the current hunk contains items that are
+different between the two sequences.  It actually returns one of the
+follow 4 values:
+
+=over 4
+
+=item 3
+
+C<3==(1|2)>.  This hunk contains items from @seq1 and the items
+from @seq2 that should replace them.  Both sequence 1 and 2
+contain changed items so both the 1 and 2 bits are set.
+
+=item 2
+
+This hunk only contains items from @seq2 that should be inserted (not
+items from @seq1).  Only sequence 2 contains changed items so only the 2
+bit is set.
+
+=item 1
+
+This hunk only contains items from @seq1 that should be deleted (not
+items from @seq2).  Only sequence 1 contains changed items so only the 1
+bit is set.
+
+=item 0
+
+This means that the items in this hunk are the same in both sequences.
+Neither sequence 1 nor 2 contain changed items so neither the 1 nor the
+2 bits are set.
+
+=back
+
+=item C<Same>
+
+C<Same> returns a true value iff the current hunk contains items that
+are the same in both sequences.  It actually returns the list of items
+if they are the same or an emty list if they aren't.  In a scalar
+context, it returns the size of the list.
+
+=item C<Items>
+
+    $count = $diff->Items(2);
+    @items = $diff->Items($seqNum);
+
+C<Items> returns the (number of) items from the specified sequence that
+are part of the current hunk.
+
+If the current hunk contains only insertions, then
+C<< $diff->Items(1) >> will return an empty list (0 in a scalar conext).
+If the current hunk contains only deletions, then C<< $diff->Items(2) >>
+will return an empty list (0 in a scalar conext).
+
+If the hunk contains replacements, then both C<< $diff->Items(1) >> and
+C<< $diff->Items(2) >> will return different, non-empty lists.
+
+Otherwise, the hunk contains identical items and all of the following
+will return the same lists:
+
+    @items = $diff->Items(1);
+    @items = $diff->Items(2);
+    @items = $diff->Same();
+
+=item C<Range>
+
+    $count = $diff->Range( $seqNum );
+    @indices = $diff->Range( $seqNum );
+    @indices = $diff->Range( $seqNum, $base );
+
+C<Range> is like C<Items> except that it returns a list of I<indices> to
+the items rather than the items themselves.  By default, the index of
+the first item (in each sequence) is 0 but this can be changed by
+calling the C<Base> method.  So, by default, the following two snippets
+return the same lists:
+
+    @list = $diff->Items(2);
+    @list = @seq2[ $diff->Range(2) ];
+
+You can also specify the base to use as the second argument.  So the
+following two snippets I<always> return the same lists:
+
+    @list = $diff->Items(1);
+    @list = @seq1[ $diff->Range(1,0) ];
+
+=item C<Base>
+
+    $curBase = $diff->Base();
+    $oldBase = $diff->Base($newBase);
+
+C<Base> sets and/or returns the current base (usually 0 or 1) that is
+used when you request range information.  The base defaults to 0 so
+that range information is returned as array indices.  You can set the
+base to 1 if you want to report traditional line numbers instead.
+
+=item C<Min>
+
+    $min1 = $diff->Min(1);
+    $min = $diff->Min( $seqNum, $base );
+
+C<Min> returns the first value that C<Range> would return (given the
+same arguments) or returns C<undef> if C<Range> would return an empty
+list.
+
+=item C<Max>
+
+C<Max> returns the last value that C<Range> would return or C<undef>.
+
+=item C<Get>
+
+    ( $n, $x, $r ) = $diff->Get(qw( min1 max1 range1 ));
+    @values = $diff->Get(qw( 0min2 1max2 range2 same base ));
+
+C<Get> returns one or more scalar values.  You pass in a list of the
+names of the values you want returned.  Each name must match one of the
+following regexes:
+
+    /^(-?\d+)?(min|max)[12]$/i
+    /^(range[12]|same|diff|base)$/i
+
+The 1 or 2 after a name says which sequence you want the information
+for (and where allowed, it is required).  The optional number before
+"min" or "max" is the base to use.  So the following equalities hold:
+
+    $diff->Get('min1') == $diff->Min(1)
+    $diff->Get('0min2') == $diff->Min(2,0)
+
+Using C<Get> in a scalar context when you've passed in more than one
+name is a fatal error (C<die> is called).
+
+=back
+
+=head2 C<prepare>
+
+Given a reference to a list of items, C<prepare> returns a reference
+to a hash which can be used when comparing this sequence to other
+sequences with C<LCS> or C<LCS_length>.
+
+    $prep = prepare( \@seq1 );
+    for $i ( 0 .. 10_000 )
+    {
+        @lcs = LCS( $prep, $seq[$i] );
+        # do something useful with @lcs
+    }
+
+C<prepare> may be passed an optional third parameter; this is a CODE
+reference to a key generation function.  See L</KEY GENERATION
+FUNCTIONS>.
+
+    $prep = prepare( \@seq1, \&keyGen );
+    for $i ( 0 .. 10_000 )
+    {
+        @lcs = LCS( $seq[$i], $prep, \&keyGen );
+        # do something useful with @lcs
+    }
+
+Using C<prepare> provides a performance gain of about 50% when calling LCS
+many times compared with not preparing.
+
+=head2 C<diff>
+
+    @diffs     = diff( \@seq1, \@seq2 );
+    $diffs_ref = diff( \@seq1, \@seq2 );
+
+C<diff> computes the smallest set of additions and deletions necessary
+to turn the first sequence into the second, and returns a description
+of these changes.  The description is a list of I<hunks>; each hunk
+represents a contiguous section of items which should be added,
+deleted, or replaced.  (Hunks containing unchanged items are not
+included.)
+
+The return value of C<diff> is a list of hunks, or, in scalar context, a
+reference to such a list.  If there are no differences, the list will be
+empty.
+
+Here is an example.  Calling C<diff> for the following two sequences:
+
+    a b c e h j l m n p
+    b c d e f j k l m r s t
+
+would produce the following list:
+
+    (
+      [ [ '-', 0, 'a' ] ],
+
+      [ [ '+', 2, 'd' ] ],
+
+      [ [ '-', 4, 'h' ],
+        [ '+', 4, 'f' ] ],
+
+      [ [ '+', 6, 'k' ] ],
+
+      [ [ '-',  8, 'n' ],
+        [ '-',  9, 'p' ],
+        [ '+',  9, 'r' ],
+        [ '+', 10, 's' ],
+        [ '+', 11, 't' ] ],
+    )
+
+There are five hunks here.  The first hunk says that the C<a> at
+position 0 of the first sequence should be deleted (C<->).  The second
+hunk says that the C<d> at position 2 of the second sequence should
+be inserted (C<+>).  The third hunk says that the C<h> at position 4
+of the first sequence should be removed and replaced with the C<f>
+from position 4 of the second sequence.  And so on.
+
+C<diff> may be passed an optional third parameter; this is a CODE
+reference to a key generation function.  See L</KEY GENERATION
+FUNCTIONS>.
+
+Additional parameters, if any, will be passed to the key generation
+routine.
+
+=head2 C<sdiff>
+
+    @sdiffs     = sdiff( \@seq1, \@seq2 );
+    $sdiffs_ref = sdiff( \@seq1, \@seq2 );
+
+C<sdiff> computes all necessary components to show two sequences
+and their minimized differences side by side, just like the
+Unix-utility I<sdiff> does:
+
+    same             same
+    before     |     after
+    old        <     -
+    -          >     new
+
+It returns a list of array refs, each pointing to an array of
+display instructions. In scalar context it returns a reference
+to such a list. If there are no differences, the list will have one
+entry per item, each indicating that the item was unchanged.
+
+Display instructions consist of three elements: A modifier indicator
+(C<+>: Element added, C<->: Element removed, C<u>: Element unmodified,
+C<c>: Element changed) and the value of the old and new elements, to
+be displayed side-by-side.
+
+An C<sdiff> of the following two sequences:
+
+    a b c e h j l m n p
+    b c d e f j k l m r s t
+
+results in
+
+    ( [ '-', 'a', ''  ],
+      [ 'u', 'b', 'b' ],
+      [ 'u', 'c', 'c' ],
+      [ '+', '',  'd' ],
+      [ 'u', 'e', 'e' ],
+      [ 'c', 'h', 'f' ],
+      [ 'u', 'j', 'j' ],
+      [ '+', '',  'k' ],
+      [ 'u', 'l', 'l' ],
+      [ 'u', 'm', 'm' ],
+      [ 'c', 'n', 'r' ],
+      [ 'c', 'p', 's' ],
+      [ '+', '',  't' ],
+    )
+
+C<sdiff> may be passed an optional third parameter; this is a CODE
+reference to a key generation function.  See L</KEY GENERATION
+FUNCTIONS>.
+
+Additional parameters, if any, will be passed to the key generation
+routine.
+
+=head2 C<compact_diff>
+
+C<compact_diff> is much like C<sdiff> except it returns a much more
+compact description consisting of just one flat list of indices.  An
+example helps explain the format:
+
+    my @a = qw( a b c   e  h j   l m n p      );
+    my @b = qw(   b c d e f  j k l m    r s t );
+    @cdiff = compact_diff( \@a, \@b );
+    # Returns:
+    #   @a      @b       @a       @b
+    #  start   start   values   values
+    (    0,      0,   #       =
+         0,      0,   #    a  !
+         1,      0,   #  b c  =  b c
+         3,      2,   #       !  d
+         3,      3,   #    e  =  e
+         4,      4,   #    f  !  h
+         5,      5,   #    j  =  j
+         6,      6,   #       !  k
+         6,      7,   #  l m  =  l m
+         8,      9,   #  n p  !  r s t
+        10,     12,   #
+    );
+
+The 0th, 2nd, 4th, etc. entries are all indices into @seq1 (@a in the
+above example) indicating where a hunk begins.  The 1st, 3rd, 5th, etc.
+entries are all indices into @seq2 (@b in the above example) indicating
+where the same hunk begins.
+
+So each pair of indices (except the last pair) describes where a hunk
+begins (in each sequence).  Since each hunk must end at the item just
+before the item that starts the next hunk, the next pair of indices can
+be used to determine where the hunk ends.
+
+So, the first 4 entries (0..3) describe the first hunk.  Entries 0 and 1
+describe where the first hunk begins (and so are always both 0).
+Entries 2 and 3 describe where the next hunk begins, so subtracting 1
+from each tells us where the first hunk ends.  That is, the first hunk
+contains items C<$diff[0]> through C<$diff[2] - 1> of the first sequence
+and contains items C<$diff[1]> through C<$diff[3] - 1> of the second
+sequence.
+
+In other words, the first hunk consists of the following two lists of items:
+
+               #  1st pair     2nd pair
+               # of indices   of indices
+    @list1 = @a[ $cdiff[0] .. $cdiff[2]-1 ];
+    @list2 = @b[ $cdiff[1] .. $cdiff[3]-1 ];
+               # Hunk start   Hunk end
+
+Note that the hunks will always alternate between those that are part of
+the LCS (those that contain unchanged items) and those that contain
+changes.  This means that all we need to be told is whether the first
+hunk is a 'same' or 'diff' hunk and we can determine which of the other
+hunks contain 'same' items or 'diff' items.
+
+By convention, we always make the first hunk contain unchanged items.
+So the 1st, 3rd, 5th, etc. hunks (all odd-numbered hunks if you start
+counting from 1) all contain unchanged items.  And the 2nd, 4th, 6th,
+etc. hunks (all even-numbered hunks if you start counting from 1) all
+contain changed items.
+
+Since @a and @b don't begin with the same value, the first hunk in our
+example is empty (otherwise we'd violate the above convention).  Note
+that the first 4 index values in our example are all zero.  Plug these
+values into our previous code block and we get:
+
+    @hunk1a = @a[ 0 .. 0-1 ];
+    @hunk1b = @b[ 0 .. 0-1 ];
+
+And C<0..-1> returns the empty list.
+
+Move down one pair of indices (2..5) and we get the offset ranges for
+the second hunk, which contains changed items.
+
+Since C<@diff[2..5]> contains (0,0,1,0) in our example, the second hunk
+consists of these two lists of items:
+
+        @hunk2a = @a[ $cdiff[2] .. $cdiff[4]-1 ];
+        @hunk2b = @b[ $cdiff[3] .. $cdiff[5]-1 ];
+    # or
+        @hunk2a = @a[ 0 .. 1-1 ];
+        @hunk2b = @b[ 0 .. 0-1 ];
+    # or
+        @hunk2a = @a[ 0 .. 0 ];
+        @hunk2b = @b[ 0 .. -1 ];
+    # or
+        @hunk2a = ( 'a' );
+        @hunk2b = ( );
+
+That is, we would delete item 0 ('a') from @a.
+
+Since C<@diff[4..7]> contains (1,0,3,2) in our example, the third hunk
+consists of these two lists of items:
+
+        @hunk3a = @a[ $cdiff[4] .. $cdiff[6]-1 ];
+        @hunk3a = @b[ $cdiff[5] .. $cdiff[7]-1 ];
+    # or
+        @hunk3a = @a[ 1 .. 3-1 ];
+        @hunk3a = @b[ 0 .. 2-1 ];
+    # or
+        @hunk3a = @a[ 1 .. 2 ];
+        @hunk3a = @b[ 0 .. 1 ];
+    # or
+        @hunk3a = qw( b c );
+        @hunk3a = qw( b c );
+
+Note that this third hunk contains unchanged items as our convention demands.
+
+You can continue this process until you reach the last two indices,
+which will always be the number of items in each sequence.  This is
+required so that subtracting one from each will give you the indices to
+the last items in each sequence.
+
+=head2 C<traverse_sequences>
+
+C<traverse_sequences> used to be the most general facility provided by
+this module (the new OO interface is more powerful and much easier to
+use).
+
+Imagine that there are two arrows.  Arrow A points to an element of
+sequence A, and arrow B points to an element of the sequence B. 
+Initially, the arrows point to the first elements of the respective
+sequences.  C<traverse_sequences> will advance the arrows through the
+sequences one element at a time, calling an appropriate user-specified
+callback function before each advance.  It willadvance the arrows in
+such a way that if there are equal elements C<$A[$i]> and C<$B[$j]>
+which are equal and which are part of the LCS, there will be some moment
+during the execution of C<traverse_sequences> when arrow A is pointing
+to C<$A[$i]> and arrow B is pointing to C<$B[$j]>.  When this happens,
+C<traverse_sequences> will call the C<MATCH> callback function and then
+it will advance both arrows.
+
+Otherwise, one of the arrows is pointing to an element of its sequence
+that is not part of the LCS.  C<traverse_sequences> will advance that
+arrow and will call the C<DISCARD_A> or the C<DISCARD_B> callback,
+depending on which arrow it advanced.  If both arrows point to elements
+that are not part of the LCS, then C<traverse_sequences> will advance
+one of them and call the appropriate callback, but it is not specified
+which it will call.
+
+The arguments to C<traverse_sequences> are the two sequences to
+traverse, and a hash which specifies the callback functions, like this:
+
+    traverse_sequences(
+        \@seq1, \@seq2,
+        {   MATCH => $callback_1,
+            DISCARD_A => $callback_2,
+            DISCARD_B => $callback_3,
+        }
+    );
+
+Callbacks for MATCH, DISCARD_A, and DISCARD_B are invoked with at least
+the indices of the two arrows as their arguments.  They are not expected
+to return any values.  If a callback is omitted from the table, it is
+not called.
+
+Callbacks for A_FINISHED and B_FINISHED are invoked with at least the
+corresponding index in A or B.
+
+If arrow A reaches the end of its sequence, before arrow B does,
+C<traverse_sequences> will call the C<A_FINISHED> callback when it
+advances arrow B, if there is such a function; if not it will call
+C<DISCARD_B> instead.  Similarly if arrow B finishes first. 
+C<traverse_sequences> returns when both arrows are at the ends of their
+respective sequences.  It returns true on success and false on failure. 
+At present there is no way to fail.
+
+C<traverse_sequences> may be passed an optional fourth parameter; this
+is a CODE reference to a key generation function.  See L</KEY GENERATION
+FUNCTIONS>.
+
+Additional parameters, if any, will be passed to the key generation function.
+
+If you want to pass additional parameters to your callbacks, but don't
+need a custom key generation function, you can get the default by
+passing undef:
+
+    traverse_sequences(
+        \@seq1, \@seq2,
+        {   MATCH => $callback_1,
+            DISCARD_A => $callback_2,
+            DISCARD_B => $callback_3,
+        },
+        undef,     # default key-gen
+        $myArgument1,
+        $myArgument2,
+        $myArgument3,
+    );
+
+C<traverse_sequences> does not have a useful return value; you are
+expected to plug in the appropriate behavior with the callback
+functions.
+
+=head2 C<traverse_balanced>
+
+C<traverse_balanced> is an alternative to C<traverse_sequences>. It
+uses a different algorithm to iterate through the entries in the
+computed LCS. Instead of sticking to one side and showing element changes
+as insertions and deletions only, it will jump back and forth between
+the two sequences and report I<changes> occurring as deletions on one
+side followed immediatly by an insertion on the other side.
+
+In addition to the C<DISCARD_A>, C<DISCARD_B>, and C<MATCH> callbacks
+supported by C<traverse_sequences>, C<traverse_balanced> supports
+a C<CHANGE> callback indicating that one element got C<replaced> by another:
+
+    traverse_balanced(
+        \@seq1, \@seq2,
+        {   MATCH => $callback_1,
+            DISCARD_A => $callback_2,
+            DISCARD_B => $callback_3,
+            CHANGE    => $callback_4,
+        }
+    );
+
+If no C<CHANGE> callback is specified, C<traverse_balanced>
+will map C<CHANGE> events to C<DISCARD_A> and C<DISCARD_B> actions,
+therefore resulting in a similar behaviour as C<traverse_sequences>
+with different order of events.
+
+C<traverse_balanced> might be a bit slower than C<traverse_sequences>,
+noticable only while processing huge amounts of data.
+
+The C<sdiff> function of this module 
+is implemented as call to C<traverse_balanced>.
+
+C<traverse_balanced> does not have a useful return value; you are expected to
+plug in the appropriate behavior with the callback functions.
+
+=head1 KEY GENERATION FUNCTIONS
+
+Most of the functions accept an optional extra parameter.  This is a
+CODE reference to a key generating (hashing) function that should return
+a string that uniquely identifies a given element.  It should be the
+case that if two elements are to be considered equal, their keys should
+be the same (and the other way around).  If no key generation function
+is provided, the key will be the element as a string.
+
+By default, comparisons will use "eq" and elements will be turned into keys
+using the default stringizing operator '""'.
+
+Where this is important is when you're comparing something other than
+strings.  If it is the case that you have multiple different objects
+that should be considered to be equal, you should supply a key
+generation function. Otherwise, you have to make sure that your arrays
+contain unique references.
+
+For instance, consider this example:
+
+    package Person;
+
+    sub new
+    {
+        my $package = shift;
+        return bless { name => '', ssn => '', @_ }, $package;
+    }
+
+    sub clone
+    {
+        my $old = shift;
+        my $new = bless { %$old }, ref($old);
+    }
+
+    sub hash
+    {
+        return shift()->{'ssn'};
+    }
+
+    my $person1 = Person->new( name => 'Joe', ssn => '123-45-6789' );
+    my $person2 = Person->new( name => 'Mary', ssn => '123-47-0000' );
+    my $person3 = Person->new( name => 'Pete', ssn => '999-45-2222' );
+    my $person4 = Person->new( name => 'Peggy', ssn => '123-45-9999' );
+    my $person5 = Person->new( name => 'Frank', ssn => '000-45-9999' );
+
+If you did this:
+
+    my $array1 = [ $person1, $person2, $person4 ];
+    my $array2 = [ $person1, $person3, $person4, $person5 ];
+    Algorithm::Diff::diff( $array1, $array2 );
+
+everything would work out OK (each of the objects would be converted
+into a string like "Person=HASH(0x82425b0)" for comparison).
+
+But if you did this:
+
+    my $array1 = [ $person1, $person2, $person4 ];
+    my $array2 = [ $person1, $person3, $person4->clone(), $person5 ];
+    Algorithm::Diff::diff( $array1, $array2 );
+
+$person4 and $person4->clone() (which have the same name and SSN)
+would be seen as different objects. If you wanted them to be considered
+equivalent, you would have to pass in a key generation function:
+
+    my $array1 = [ $person1, $person2, $person4 ];
+    my $array2 = [ $person1, $person3, $person4->clone(), $person5 ];
+    Algorithm::Diff::diff( $array1, $array2, \&Person::hash );
+
+This would use the 'ssn' field in each Person as a comparison key, and
+so would consider $person4 and $person4->clone() as equal.
+
+You may also pass additional parameters to the key generation function
+if you wish.
+
+=head1 ERROR CHECKING
+
+If you pass these routines a non-reference and they expect a reference,
+they will die with a message.
+
+=head1 AUTHOR
+
+This version released by Tye McQueen (http://perlmonks.org/?node=tye).
+
+=head1 LICENSE
+
+Parts Copyright (c) 2000-2004 Ned Konz.  All rights reserved.
+Parts by Tye McQueen.
+
+This program is free software; you can redistribute it and/or modify it
+under the same terms as Perl.
+
+=head1 MAILING LIST
+
+Mark-Jason still maintains a mailing list.  To join a low-volume mailing
+list for announcements related to diff and Algorithm::Diff, send an
+empty mail message to mjd-perl-diff-request@plover.com.
+
+=head1 CREDITS
+
+Versions through 0.59 (and much of this documentation) were written by:
+
+Mark-Jason Dominus, mjd-perl-diff@plover.com
+
+This version borrows some documentation and routine names from
+Mark-Jason's, but Diff.pm's code was completely replaced.
+
+This code was adapted from the Smalltalk code of Mario Wolczko
+<mario@wolczko.com>, which is available at
+ftp://st.cs.uiuc.edu/pub/Smalltalk/MANCHESTER/manchester/4.0/diff.st
+
+C<sdiff> and C<traverse_balanced> were written by Mike Schilli
+<m@perlmeister.com>.
+
+The algorithm is that described in
+I<A Fast Algorithm for Computing Longest Common Subsequences>,
+CACM, vol.20, no.5, pp.350-353, May 1977, with a few
+minor improvements to improve the speed.
+
+Much work was done by Ned Konz (perl@bike-nomad.com).
+
+The OO interface and some other changes are by Tye McQueen.
+
+=cut
diff --git a/src/tests/Make.tests b/src/tests/Make.tests
new file mode 100644
index 0000000..32494ca
--- /dev/null
+++ b/src/tests/Make.tests
@@ -0,0 +1,77 @@
+# -*- makefile -*-
+
+include $(patsubst %,$(SRCDIR)/%/Make.tests,$(TEST_SUBDIRS))
+
+PROGS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_PROGS))
+TESTS = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_TESTS))
+EXTRA_GRADES = $(foreach subdir,$(TEST_SUBDIRS),$($(subdir)_EXTRA_GRADES))
+
+OUTPUTS = $(addsuffix .output,$(TESTS) $(EXTRA_GRADES))
+ERRORS = $(addsuffix .errors,$(TESTS) $(EXTRA_GRADES))
+RESULTS = $(addsuffix .result,$(TESTS) $(EXTRA_GRADES))
+
+ifdef PROGS
+include ../../Makefile.userprog
+endif
+
+TIMEOUT = 60
+
+clean::
+	rm -f $(OUTPUTS) $(ERRORS) $(RESULTS) 
+
+grade:: results
+	$(SRCDIR)/tests/make-grade $(SRCDIR) $< $(GRADING_FILE) | tee $@
+
+check:: results
+	@cat $<
+	@COUNT="`egrep '^(pass|FAIL) ' $< | wc -l | sed 's/[ 	]//g;'`"; \
+	FAILURES="`egrep '^FAIL ' $< | wc -l | sed 's/[ 	]//g;'`"; \
+	if [ $$FAILURES = 0 ]; then					  \
+		echo "All $$COUNT tests passed.";			  \
+	else								  \
+		echo "$$FAILURES of $$COUNT tests failed.";		  \
+		exit 1;							  \
+	fi
+
+results: $(RESULTS)
+	@for d in $(TESTS) $(EXTRA_GRADES); do			\
+		if echo PASS | cmp -s $$d.result -; then	\
+			echo "pass $$d";			\
+		else						\
+			echo "FAIL $$d";			\
+		fi;						\
+	done > $@
+
+outputs:: $(OUTPUTS)
+
+$(foreach prog,$(PROGS),$(eval $(prog).output: $(prog)))
+$(foreach test,$(TESTS),$(eval $(test).output: $($(test)_PUTFILES)))
+$(foreach test,$(TESTS),$(eval $(test).output: TEST = $(test)))
+$(foreach test,$(TESTS),$(eval $(test).result: $(test).output $(test).ck))
+
+# Prevent an environment variable VERBOSE from surprising us.
+VERBOSE =
+
+TESTCMD = pintos -v -k -T $(TIMEOUT)
+TESTCMD += $(SIMULATOR)
+TESTCMD += $(PINTOSOPTS)
+ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
+TESTCMD += $(FILESYSSOURCE)
+TESTCMD += $(foreach file,$(PUTFILES),-p $(file) -a $(notdir $(file)))
+endif
+ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
+TESTCMD += --swap-size=4
+endif
+TESTCMD += -- -q
+TESTCMD += $(KERNELFLAGS)
+ifeq ($(filter userprog, $(KERNEL_SUBDIRS)), userprog)
+TESTCMD += -f
+endif
+TESTCMD += $(if $($(TEST)_ARGS),run '$(*F) $($(TEST)_ARGS)',run $(*F))
+TESTCMD += < /dev/null
+TESTCMD += 2> $(TEST).errors $(if $(VERBOSE),|tee,>) $(TEST).output
+%.output: kernel.bin loader.bin
+	$(TESTCMD)
+
+%.result: %.ck %.output
+	perl -I$(SRCDIR) $< $* $@
diff --git a/src/tests/arc4.c b/src/tests/arc4.c
new file mode 100644
index 0000000..b033cc6
--- /dev/null
+++ b/src/tests/arc4.c
@@ -0,0 +1,53 @@
+#include <stdint.h>
+#include "tests/arc4.h"
+
+/* Swap bytes. */
+static inline void
+swap_byte (uint8_t *a, uint8_t *b)
+{
+  uint8_t t = *a;
+  *a = *b;
+  *b = t;
+}
+
+void
+arc4_init (struct arc4 *arc4, const void *key_, size_t size)
+{
+  const uint8_t *key = key_;
+  size_t key_idx;
+  uint8_t *s;
+  int i, j;
+
+  s = arc4->s;
+  arc4->i = arc4->j = 0;
+  for (i = 0; i < 256; i++)
+    s[i] = i;
+  for (key_idx = 0, i = j = 0; i < 256; i++)
+    {
+      j = (j + s[i] + key[key_idx]) & 255;
+      swap_byte (s + i, s + j);
+      if (++key_idx >= size)
+        key_idx = 0;
+    }
+}
+
+void
+arc4_crypt (struct arc4 *arc4, void *buf_, size_t size)
+{
+  uint8_t *buf = buf_;
+  uint8_t *s;
+  uint8_t i, j;
+
+  s = arc4->s;
+  i = arc4->i;
+  j = arc4->j;
+  while (size-- > 0)
+    {
+      i += 1;
+      j += s[i];
+      swap_byte (s + i, s + j);
+      *buf++ ^= s[(s[i] + s[j]) & 255];
+    }
+  arc4->i = i;
+  arc4->j = j;
+}
diff --git a/src/tests/arc4.h b/src/tests/arc4.h
new file mode 100644
index 0000000..61c533a
--- /dev/null
+++ b/src/tests/arc4.h
@@ -0,0 +1,17 @@
+#ifndef TESTS_ARC4_H
+#define TESTS_ARC4_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Alleged RC4 algorithm encryption state. */
+struct arc4
+  {
+    uint8_t s[256];
+    uint8_t i, j;
+  };
+
+void arc4_init (struct arc4 *, const void *, size_t);
+void arc4_crypt (struct arc4 *, void *, size_t);
+
+#endif /* tests/arc4.h */
diff --git a/src/tests/arc4.pm b/src/tests/arc4.pm
new file mode 100644
index 0000000..df19216
--- /dev/null
+++ b/src/tests/arc4.pm
@@ -0,0 +1,29 @@
+use strict;
+use warnings;
+
+sub arc4_init {
+    my ($key) = @_;
+    my (@s) = 0...255;
+    my ($j) = 0;
+    for my $i (0...255) {
+	$j = ($j + $s[$i] + ord (substr ($key, $i % length ($key), 1))) & 0xff;
+	@s[$i, $j] = @s[$j, $i];
+    }
+    return (0, 0, @s);
+}
+
+sub arc4_crypt {
+    my ($arc4, $buf) = @_;
+    my ($i, $j, @s) = @$arc4;
+    my ($out) = "";
+    for my $c (split (//, $buf)) {
+	$i = ($i + 1) & 0xff;
+	$j = ($j + $s[$i]) & 0xff;
+	@s[$i, $j] = @s[$j, $i];
+	$out .= chr (ord ($c) ^ $s[($s[$i] + $s[$j]) & 0xff]);
+    }
+    @$arc4 = ($i, $j, @s);
+    return $out;
+}
+
+1;
diff --git a/src/tests/cksum.c b/src/tests/cksum.c
new file mode 100644
index 0000000..92a2995
--- /dev/null
+++ b/src/tests/cksum.c
@@ -0,0 +1,92 @@
+/* crctab[] and cksum() are from the `cksum' entry in SUSv3. */
+
+#include <stdint.h>
+#include "tests/cksum.h"
+
+static unsigned long crctab[] = {
+  0x00000000,
+  0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+  0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+  0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+  0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+  0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
+  0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+  0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+  0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+  0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
+  0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+  0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+  0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+  0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+  0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+  0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+  0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+  0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
+  0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+  0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+  0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+  0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
+  0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+  0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+  0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+  0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+  0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+  0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+  0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+  0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
+  0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+  0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+  0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+  0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
+  0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+  0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+  0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+  0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+  0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+  0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+  0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+  0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
+  0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+  0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+  0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+  0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
+  0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+  0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+  0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+  0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+  0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+  0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+};
+
+/* This is the algorithm used by the Posix `cksum' utility. */
+unsigned long
+cksum (const void *b_, size_t n)
+{
+  const unsigned char *b = b_;
+  uint32_t s = 0;
+  size_t i;
+  for (i = n; i > 0; --i)
+    {
+      unsigned char c = *b++;
+      s = (s << 8) ^ crctab[(s >> 24) ^ c];
+    }
+  while (n != 0)
+    {
+      unsigned char c = n;
+      n >>= 8;
+      s = (s << 8) ^ crctab[(s >> 24) ^ c];
+    }
+  return ~s;
+}
+
+#ifdef STANDALONE_TEST
+#include <stdio.h>
+int
+main (void) 
+{
+  char buf[65536];
+  int n = fread (buf, 1, sizeof buf, stdin);
+  printf ("%lu\n", cksum (buf, n));
+  return 0;
+}
+#endif
diff --git a/src/tests/cksum.h b/src/tests/cksum.h
new file mode 100644
index 0000000..23a1fe9
--- /dev/null
+++ b/src/tests/cksum.h
@@ -0,0 +1,8 @@
+#ifndef TESTS_CKSUM_H
+#define TESTS_CKSUM_H
+
+#include <stddef.h>
+
+unsigned long cksum(const void *, size_t);
+
+#endif /* tests/cksum.h */
diff --git a/src/tests/cksum.pm b/src/tests/cksum.pm
new file mode 100644
index 0000000..73be5f2
--- /dev/null
+++ b/src/tests/cksum.pm
@@ -0,0 +1,87 @@
+# From the `cksum' entry in SUSv3.
+
+use strict;
+use warnings;
+
+my (@crctab) =
+  (0x00000000,
+   0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
+   0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6,
+   0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+   0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac,
+   0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f,
+   0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a,
+   0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+   0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58,
+   0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033,
+   0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe,
+   0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+   0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4,
+   0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
+   0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5,
+   0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+   0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07,
+   0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c,
+   0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1,
+   0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+   0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b,
+   0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698,
+   0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d,
+   0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+   0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f,
+   0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
+   0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80,
+   0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+   0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a,
+   0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629,
+   0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c,
+   0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+   0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e,
+   0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65,
+   0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8,
+   0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+   0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2,
+   0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
+   0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74,
+   0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+   0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21,
+   0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a,
+   0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087,
+   0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+   0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d,
+   0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce,
+   0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb,
+   0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+   0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09,
+   0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
+   0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf,
+   0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4);
+
+sub cksum {
+    my ($b) = @_;
+    my ($n) = length ($b);
+    my ($s) = 0;
+    for my $i (0...$n - 1) {
+	my ($c) = ord (substr ($b, $i, 1));
+	$s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
+	$s &= 0xffff_ffff;
+    }
+    while ($n != 0) {
+	my ($c) = $n & 0xff;
+	$n >>= 8;
+	$s = ($s << 8) ^ $crctab[($s >> 24) ^ $c];
+	$s &= 0xffff_ffff;
+    }
+    return ~$s & 0xffff_ffff;
+}
+
+sub cksum_file {
+    my ($file) = @_;
+    open (FILE, '<', $file) or die "$file: open: $!\n";
+    my ($data);
+    sysread (FILE, $data, -s FILE) == -s FILE or die "$file: read: $!\n";
+    close (FILE);
+    return cksum ($data);
+}
+
+1;
diff --git a/src/tests/filesys/Grading.no-vm b/src/tests/filesys/Grading.no-vm
new file mode 100644
index 0000000..ee98fc1
--- /dev/null
+++ b/src/tests/filesys/Grading.no-vm
@@ -0,0 +1,18 @@
+# Percentage of the testing point total designated for each set of
+# tests.
+
+# This project is primarily about implementing the file system, but
+# all the previous functionality should work too.  It's not too easy
+# to screw it up, thus the emphasis.
+
+# 65% for extended file system features.
+30%	tests/filesys/extended/Rubric.functionality
+15%	tests/filesys/extended/Rubric.robustness
+20%	tests/filesys/extended/Rubric.persistence
+
+# 20% to not break the provided file system features.
+20%	tests/filesys/base/Rubric
+
+# 15% for the rest.
+10%	tests/userprog/Rubric.functionality
+5%	tests/userprog/Rubric.robustness
diff --git a/src/tests/filesys/Grading.with-vm b/src/tests/filesys/Grading.with-vm
new file mode 100644
index 0000000..e7c041e
--- /dev/null
+++ b/src/tests/filesys/Grading.with-vm
@@ -0,0 +1,22 @@
+# Percentage of the testing point total designated for each set of
+# tests.
+
+# This project is primarily about implementing the file system, but
+# all the previous functionality should work too.  It's not too easy
+# to screw it up, thus the emphasis.
+
+# 65% for extended file system features.
+30%	tests/filesys/extended/Rubric.functionality
+15%	tests/filesys/extended/Rubric.robustness
+20%	tests/filesys/extended/Rubric.persistence
+
+# 20% to not break the provided file system features.
+20%	tests/filesys/base/Rubric
+
+# 15% for the rest.
+10%	tests/userprog/Rubric.functionality
+5%	tests/userprog/Rubric.robustness
+
+# Up to 10% bonus for working VM functionality.
+8%	tests/vm/Rubric.functionality
+2%	tests/vm/Rubric.robustness
diff --git a/src/tests/filesys/base/Make.tests b/src/tests/filesys/base/Make.tests
new file mode 100644
index 0000000..e475222
--- /dev/null
+++ b/src/tests/filesys/base/Make.tests
@@ -0,0 +1,18 @@
+# -*- makefile -*-
+
+tests/filesys/base_TESTS = $(addprefix tests/filesys/base/,lg-create	\
+lg-full lg-random lg-seq-block lg-seq-random sm-create sm-full		\
+sm-random sm-seq-block sm-seq-random syn-read syn-remove syn-write)
+
+tests/filesys/base_PROGS = $(tests/filesys/base_TESTS) $(addprefix	\
+tests/filesys/base/,child-syn-read child-syn-wrt)
+
+$(foreach prog,$(tests/filesys/base_PROGS),				\
+	$(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
+$(foreach prog,$(tests/filesys/base_TESTS),			\
+	$(eval $(prog)_SRC += tests/main.c))
+
+tests/filesys/base/syn-read_PUTFILES = tests/filesys/base/child-syn-read
+tests/filesys/base/syn-write_PUTFILES = tests/filesys/base/child-syn-wrt
+
+tests/filesys/base/syn-read.output: TIMEOUT = 300
diff --git a/src/tests/filesys/base/Rubric b/src/tests/filesys/base/Rubric
new file mode 100644
index 0000000..49a9d15
--- /dev/null
+++ b/src/tests/filesys/base/Rubric
@@ -0,0 +1,19 @@
+Functionality of base file system:
+- Test basic support for small files.
+1	sm-create
+2	sm-full
+2	sm-random
+2	sm-seq-block
+3	sm-seq-random
+
+- Test basic support for large files.
+1	lg-create
+2	lg-full
+2	lg-random
+2	lg-seq-block
+3	lg-seq-random
+
+- Test synchronized multiprogram access to files.
+4	syn-read
+4	syn-write
+2	syn-remove
diff --git a/src/tests/filesys/base/child-syn-read.c b/src/tests/filesys/base/child-syn-read.c
new file mode 100644
index 0000000..27ee59d
--- /dev/null
+++ b/src/tests/filesys/base/child-syn-read.c
@@ -0,0 +1,43 @@
+/* Child process for syn-read test.
+   Reads the contents of a test file a byte at a time, in the
+   hope that this will take long enough that we can get a
+   significant amount of contention in the kernel file system
+   code. */
+
+#include <random.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/filesys/base/syn-read.h"
+
+static char buf[BUF_SIZE];
+
+int
+main (int argc, const char *argv[]) 
+{
+  int child_idx;
+  int fd;
+  size_t i;
+
+  test_name = "child-syn-read";
+  quiet = true;
+  
+  CHECK (argc == 2, "argc must be 2, actually %d", argc);
+  child_idx = atoi (argv[1]);
+
+  random_init (0);
+  random_bytes (buf, sizeof buf);
+
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+  for (i = 0; i < sizeof buf; i++) 
+    {
+      char c;
+      CHECK (read (fd, &c, 1) > 0, "read \"%s\"", file_name);
+      compare_bytes (&c, buf + i, 1, i, file_name);
+    }
+  close (fd);
+
+  return child_idx;
+}
+
diff --git a/src/tests/filesys/base/child-syn-wrt.c b/src/tests/filesys/base/child-syn-wrt.c
new file mode 100644
index 0000000..1b52584
--- /dev/null
+++ b/src/tests/filesys/base/child-syn-wrt.c
@@ -0,0 +1,35 @@
+/* Child process for syn-read test.
+   Writes into part of a test file.  Other processes will be
+   writing into other parts at the same time. */
+
+#include <random.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/filesys/base/syn-write.h"
+
+char buf[BUF_SIZE];
+
+int
+main (int argc, char *argv[])
+{
+  int child_idx;
+  int fd;
+
+  quiet = true;
+  
+  CHECK (argc == 2, "argc must be 2, actually %d", argc);
+  child_idx = atoi (argv[1]);
+
+  random_init (0);
+  random_bytes (buf, sizeof buf);
+
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+  seek (fd, CHUNK_SIZE * child_idx);
+  CHECK (write (fd, buf + CHUNK_SIZE * child_idx, CHUNK_SIZE) > 0,
+         "write \"%s\"", file_name);
+  msg ("close \"%s\"", file_name);
+  close (fd);
+
+  return child_idx;
+}
diff --git a/src/tests/filesys/base/full.inc b/src/tests/filesys/base/full.inc
new file mode 100644
index 0000000..38a0396
--- /dev/null
+++ b/src/tests/filesys/base/full.inc
@@ -0,0 +1,20 @@
+/* -*- c -*- */
+
+#include "tests/filesys/seq-test.h"
+#include "tests/main.h"
+
+static char buf[TEST_SIZE];
+
+static size_t
+return_test_size (void) 
+{
+  return TEST_SIZE;
+}
+
+void
+test_main (void) 
+{
+  seq_test ("quux",
+            buf, sizeof buf, sizeof buf,
+            return_test_size, NULL);
+}
diff --git a/src/tests/filesys/base/lg-create.c b/src/tests/filesys/base/lg-create.c
new file mode 100644
index 0000000..5c45eee
--- /dev/null
+++ b/src/tests/filesys/base/lg-create.c
@@ -0,0 +1,5 @@
+/* Tests that create properly zeros out the contents of a fairly
+   large file. */
+
+#define TEST_SIZE 75678
+#include "tests/filesys/create.inc"
diff --git a/src/tests/filesys/base/lg-create.ck b/src/tests/filesys/base/lg-create.ck
new file mode 100644
index 0000000..86b2c51
--- /dev/null
+++ b/src/tests/filesys/base/lg-create.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(lg-create) begin
+(lg-create) create "blargle"
+(lg-create) open "blargle" for verification
+(lg-create) verified contents of "blargle"
+(lg-create) close "blargle"
+(lg-create) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/lg-full.c b/src/tests/filesys/base/lg-full.c
new file mode 100644
index 0000000..5f7234d
--- /dev/null
+++ b/src/tests/filesys/base/lg-full.c
@@ -0,0 +1,6 @@
+/* Writes out the contents of a fairly large file all at once,
+   and then reads it back to make sure that it was written
+   properly. */
+
+#define TEST_SIZE 75678
+#include "tests/filesys/base/full.inc"
diff --git a/src/tests/filesys/base/lg-full.ck b/src/tests/filesys/base/lg-full.ck
new file mode 100644
index 0000000..ee6c7f9
--- /dev/null
+++ b/src/tests/filesys/base/lg-full.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(lg-full) begin
+(lg-full) create "quux"
+(lg-full) open "quux"
+(lg-full) writing "quux"
+(lg-full) close "quux"
+(lg-full) open "quux" for verification
+(lg-full) verified contents of "quux"
+(lg-full) close "quux"
+(lg-full) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/lg-random.c b/src/tests/filesys/base/lg-random.c
new file mode 100644
index 0000000..b6f8873
--- /dev/null
+++ b/src/tests/filesys/base/lg-random.c
@@ -0,0 +1,7 @@
+/* Writes out the content of a fairly large file in random order,
+   then reads it back in random order to verify that it was
+   written properly. */
+
+#define BLOCK_SIZE 512
+#define TEST_SIZE (512 * 150)
+#include "tests/filesys/base/random.inc"
diff --git a/src/tests/filesys/base/lg-random.ck b/src/tests/filesys/base/lg-random.ck
new file mode 100644
index 0000000..dd9f1dd
--- /dev/null
+++ b/src/tests/filesys/base/lg-random.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(lg-random) begin
+(lg-random) create "bazzle"
+(lg-random) open "bazzle"
+(lg-random) write "bazzle" in random order
+(lg-random) read "bazzle" in random order
+(lg-random) close "bazzle"
+(lg-random) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/lg-seq-block.c b/src/tests/filesys/base/lg-seq-block.c
new file mode 100644
index 0000000..580c30b
--- /dev/null
+++ b/src/tests/filesys/base/lg-seq-block.c
@@ -0,0 +1,7 @@
+/* Writes out a fairly large file sequentially, one fixed-size
+   block at a time, then reads it back to verify that it was
+   written properly. */
+
+#define TEST_SIZE 75678
+#define BLOCK_SIZE 513
+#include "tests/filesys/base/seq-block.inc"
diff --git a/src/tests/filesys/base/lg-seq-block.ck b/src/tests/filesys/base/lg-seq-block.ck
new file mode 100644
index 0000000..b789081
--- /dev/null
+++ b/src/tests/filesys/base/lg-seq-block.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(lg-seq-block) begin
+(lg-seq-block) create "noodle"
+(lg-seq-block) open "noodle"
+(lg-seq-block) writing "noodle"
+(lg-seq-block) close "noodle"
+(lg-seq-block) open "noodle" for verification
+(lg-seq-block) verified contents of "noodle"
+(lg-seq-block) close "noodle"
+(lg-seq-block) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/lg-seq-random.c b/src/tests/filesys/base/lg-seq-random.c
new file mode 100644
index 0000000..fbb6bba
--- /dev/null
+++ b/src/tests/filesys/base/lg-seq-random.c
@@ -0,0 +1,6 @@
+/* Writes out a fairly large file sequentially, one random-sized
+   block at a time, then reads it back to verify that it was
+   written properly. */
+
+#define TEST_SIZE 75678
+#include "tests/filesys/base/seq-random.inc"
diff --git a/src/tests/filesys/base/lg-seq-random.ck b/src/tests/filesys/base/lg-seq-random.ck
new file mode 100644
index 0000000..6b2dc82
--- /dev/null
+++ b/src/tests/filesys/base/lg-seq-random.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(lg-seq-random) begin
+(lg-seq-random) create "nibble"
+(lg-seq-random) open "nibble"
+(lg-seq-random) writing "nibble"
+(lg-seq-random) close "nibble"
+(lg-seq-random) open "nibble" for verification
+(lg-seq-random) verified contents of "nibble"
+(lg-seq-random) close "nibble"
+(lg-seq-random) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/random.inc b/src/tests/filesys/base/random.inc
new file mode 100644
index 0000000..eeeea68
--- /dev/null
+++ b/src/tests/filesys/base/random.inc
@@ -0,0 +1,59 @@
+/* -*- c -*- */
+
+#include <random.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#if TEST_SIZE % BLOCK_SIZE != 0
+#error TEST_SIZE must be a multiple of BLOCK_SIZE
+#endif
+
+#define BLOCK_CNT (TEST_SIZE / BLOCK_SIZE)
+
+char buf[TEST_SIZE];
+int order[BLOCK_CNT];
+
+void
+test_main (void) 
+{
+  const char *file_name = "bazzle";
+  int fd;
+  size_t i;
+
+  random_init (57);
+  random_bytes (buf, sizeof buf);
+
+  for (i = 0; i < BLOCK_CNT; i++)
+    order[i] = i;
+
+  CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+
+  msg ("write \"%s\" in random order", file_name);
+  shuffle (order, BLOCK_CNT, sizeof *order);
+  for (i = 0; i < BLOCK_CNT; i++) 
+    {
+      size_t ofs = BLOCK_SIZE * order[i];
+      seek (fd, ofs);
+      if (write (fd, buf + ofs, BLOCK_SIZE) != BLOCK_SIZE)
+        fail ("write %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
+    }
+
+  msg ("read \"%s\" in random order", file_name);
+  shuffle (order, BLOCK_CNT, sizeof *order);
+  for (i = 0; i < BLOCK_CNT; i++) 
+    {
+      char block[BLOCK_SIZE];
+      size_t ofs = BLOCK_SIZE * order[i];
+      seek (fd, ofs);
+      if (read (fd, block, BLOCK_SIZE) != BLOCK_SIZE)
+        fail ("read %d bytes at offset %zu failed", (int) BLOCK_SIZE, ofs);
+      compare_bytes (block, buf + ofs, BLOCK_SIZE, ofs, file_name);
+    }
+
+  msg ("close \"%s\"", file_name);
+  close (fd);
+}
diff --git a/src/tests/filesys/base/seq-block.inc b/src/tests/filesys/base/seq-block.inc
new file mode 100644
index 0000000..d4c1f57
--- /dev/null
+++ b/src/tests/filesys/base/seq-block.inc
@@ -0,0 +1,20 @@
+/* -*- c -*- */
+
+#include "tests/filesys/seq-test.h"
+#include "tests/main.h"
+
+static char buf[TEST_SIZE];
+
+static size_t
+return_block_size (void) 
+{
+  return BLOCK_SIZE;
+}
+
+void
+test_main (void) 
+{
+  seq_test ("noodle",
+            buf, sizeof buf, sizeof buf,
+            return_block_size, NULL);
+}
diff --git a/src/tests/filesys/base/seq-random.inc b/src/tests/filesys/base/seq-random.inc
new file mode 100644
index 0000000..a4da4c5
--- /dev/null
+++ b/src/tests/filesys/base/seq-random.inc
@@ -0,0 +1,22 @@
+/* -*- c -*- */
+
+#include <random.h>
+#include "tests/filesys/seq-test.h"
+#include "tests/main.h"
+
+static char buf[TEST_SIZE];
+
+static size_t
+return_random (void) 
+{
+  return random_ulong () % 1031 + 1;
+}
+
+void
+test_main (void) 
+{
+  random_init (-1);
+  seq_test ("nibble",
+            buf, sizeof buf, sizeof buf,
+            return_random, NULL);
+}
diff --git a/src/tests/filesys/base/sm-create.c b/src/tests/filesys/base/sm-create.c
new file mode 100644
index 0000000..6b97ac1
--- /dev/null
+++ b/src/tests/filesys/base/sm-create.c
@@ -0,0 +1,5 @@
+/* Tests that create properly zeros out the contents of a fairly
+   small file. */
+
+#define TEST_SIZE 5678
+#include "tests/filesys/create.inc"
diff --git a/src/tests/filesys/base/sm-create.ck b/src/tests/filesys/base/sm-create.ck
new file mode 100644
index 0000000..8ca80dc
--- /dev/null
+++ b/src/tests/filesys/base/sm-create.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(sm-create) begin
+(sm-create) create "blargle"
+(sm-create) open "blargle" for verification
+(sm-create) verified contents of "blargle"
+(sm-create) close "blargle"
+(sm-create) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/sm-full.c b/src/tests/filesys/base/sm-full.c
new file mode 100644
index 0000000..23ff3d4
--- /dev/null
+++ b/src/tests/filesys/base/sm-full.c
@@ -0,0 +1,6 @@
+/* Writes out the contents of a fairly small file all at once,
+   and then reads it back to make sure that it was written
+   properly. */
+
+#define TEST_SIZE 5678
+#include "tests/filesys/base/full.inc"
diff --git a/src/tests/filesys/base/sm-full.ck b/src/tests/filesys/base/sm-full.ck
new file mode 100644
index 0000000..2e0eb36
--- /dev/null
+++ b/src/tests/filesys/base/sm-full.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(sm-full) begin
+(sm-full) create "quux"
+(sm-full) open "quux"
+(sm-full) writing "quux"
+(sm-full) close "quux"
+(sm-full) open "quux" for verification
+(sm-full) verified contents of "quux"
+(sm-full) close "quux"
+(sm-full) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/sm-random.c b/src/tests/filesys/base/sm-random.c
new file mode 100644
index 0000000..42d670f
--- /dev/null
+++ b/src/tests/filesys/base/sm-random.c
@@ -0,0 +1,7 @@
+/* Writes out the content of a fairly small file in random order,
+   then reads it back in random order to verify that it was
+   written properly. */
+
+#define BLOCK_SIZE 13
+#define TEST_SIZE (13 * 123)
+#include "tests/filesys/base/random.inc"
diff --git a/src/tests/filesys/base/sm-random.ck b/src/tests/filesys/base/sm-random.ck
new file mode 100644
index 0000000..bda049d
--- /dev/null
+++ b/src/tests/filesys/base/sm-random.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(sm-random) begin
+(sm-random) create "bazzle"
+(sm-random) open "bazzle"
+(sm-random) write "bazzle" in random order
+(sm-random) read "bazzle" in random order
+(sm-random) close "bazzle"
+(sm-random) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/sm-seq-block.c b/src/tests/filesys/base/sm-seq-block.c
new file mode 100644
index 0000000..e368327
--- /dev/null
+++ b/src/tests/filesys/base/sm-seq-block.c
@@ -0,0 +1,7 @@
+/* Writes out a fairly small file sequentially, one fixed-size
+   block at a time, then reads it back to verify that it was
+   written properly. */
+
+#define TEST_SIZE 5678
+#define BLOCK_SIZE 513
+#include "tests/filesys/base/seq-block.inc"
diff --git a/src/tests/filesys/base/sm-seq-block.ck b/src/tests/filesys/base/sm-seq-block.ck
new file mode 100644
index 0000000..0e2939d
--- /dev/null
+++ b/src/tests/filesys/base/sm-seq-block.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(sm-seq-block) begin
+(sm-seq-block) create "noodle"
+(sm-seq-block) open "noodle"
+(sm-seq-block) writing "noodle"
+(sm-seq-block) close "noodle"
+(sm-seq-block) open "noodle" for verification
+(sm-seq-block) verified contents of "noodle"
+(sm-seq-block) close "noodle"
+(sm-seq-block) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/sm-seq-random.c b/src/tests/filesys/base/sm-seq-random.c
new file mode 100644
index 0000000..89e5b71
--- /dev/null
+++ b/src/tests/filesys/base/sm-seq-random.c
@@ -0,0 +1,6 @@
+/* Writes out a fairly large file sequentially, one random-sized
+   block at a time, then reads it back to verify that it was
+   written properly. */
+
+#define TEST_SIZE 5678
+#include "tests/filesys/base/seq-random.inc"
diff --git a/src/tests/filesys/base/sm-seq-random.ck b/src/tests/filesys/base/sm-seq-random.ck
new file mode 100644
index 0000000..2fb368b
--- /dev/null
+++ b/src/tests/filesys/base/sm-seq-random.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(sm-seq-random) begin
+(sm-seq-random) create "nibble"
+(sm-seq-random) open "nibble"
+(sm-seq-random) writing "nibble"
+(sm-seq-random) close "nibble"
+(sm-seq-random) open "nibble" for verification
+(sm-seq-random) verified contents of "nibble"
+(sm-seq-random) close "nibble"
+(sm-seq-random) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/syn-read.c b/src/tests/filesys/base/syn-read.c
new file mode 100644
index 0000000..7c36a42
--- /dev/null
+++ b/src/tests/filesys/base/syn-read.c
@@ -0,0 +1,31 @@
+/* Spawns 10 child processes, all of which read from the same
+   file and make sure that the contents are what they should
+   be. */
+
+#include <random.h>
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+#include "tests/filesys/base/syn-read.h"
+
+static char buf[BUF_SIZE];
+
+#define CHILD_CNT 10
+
+void
+test_main (void) 
+{
+  pid_t children[CHILD_CNT];
+  int fd;
+
+  CHECK (create (file_name, sizeof buf), "create \"%s\"", file_name);
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+  random_bytes (buf, sizeof buf);
+  CHECK (write (fd, buf, sizeof buf) > 0, "write \"%s\"", file_name);
+  msg ("close \"%s\"", file_name);
+  close (fd);
+
+  exec_children ("child-syn-read", children, CHILD_CNT);
+  wait_children (children, CHILD_CNT);
+}
diff --git a/src/tests/filesys/base/syn-read.ck b/src/tests/filesys/base/syn-read.ck
new file mode 100644
index 0000000..e2f68e8
--- /dev/null
+++ b/src/tests/filesys/base/syn-read.ck
@@ -0,0 +1,33 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(syn-read) begin
+(syn-read) create "data"
+(syn-read) open "data"
+(syn-read) write "data"
+(syn-read) close "data"
+(syn-read) exec child 1 of 10: "child-syn-read 0"
+(syn-read) exec child 2 of 10: "child-syn-read 1"
+(syn-read) exec child 3 of 10: "child-syn-read 2"
+(syn-read) exec child 4 of 10: "child-syn-read 3"
+(syn-read) exec child 5 of 10: "child-syn-read 4"
+(syn-read) exec child 6 of 10: "child-syn-read 5"
+(syn-read) exec child 7 of 10: "child-syn-read 6"
+(syn-read) exec child 8 of 10: "child-syn-read 7"
+(syn-read) exec child 9 of 10: "child-syn-read 8"
+(syn-read) exec child 10 of 10: "child-syn-read 9"
+(syn-read) wait for child 1 of 10 returned 0 (expected 0)
+(syn-read) wait for child 2 of 10 returned 1 (expected 1)
+(syn-read) wait for child 3 of 10 returned 2 (expected 2)
+(syn-read) wait for child 4 of 10 returned 3 (expected 3)
+(syn-read) wait for child 5 of 10 returned 4 (expected 4)
+(syn-read) wait for child 6 of 10 returned 5 (expected 5)
+(syn-read) wait for child 7 of 10 returned 6 (expected 6)
+(syn-read) wait for child 8 of 10 returned 7 (expected 7)
+(syn-read) wait for child 9 of 10 returned 8 (expected 8)
+(syn-read) wait for child 10 of 10 returned 9 (expected 9)
+(syn-read) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/syn-read.h b/src/tests/filesys/base/syn-read.h
new file mode 100644
index 0000000..bff8082
--- /dev/null
+++ b/src/tests/filesys/base/syn-read.h
@@ -0,0 +1,7 @@
+#ifndef TESTS_FILESYS_BASE_SYN_READ_H
+#define TESTS_FILESYS_BASE_SYN_READ_H
+
+#define BUF_SIZE 1024
+static const char file_name[] = "data";
+
+#endif /* tests/filesys/base/syn-read.h */
diff --git a/src/tests/filesys/base/syn-remove.c b/src/tests/filesys/base/syn-remove.c
new file mode 100644
index 0000000..c9ba110
--- /dev/null
+++ b/src/tests/filesys/base/syn-remove.c
@@ -0,0 +1,30 @@
+/* Verifies that a deleted file may still be written to and read
+   from. */
+
+#include <random.h>
+#include <string.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+char buf1[1234];
+char buf2[1234];
+
+void
+test_main (void) 
+{
+  const char *file_name = "deleteme";
+  int fd;
+  
+  CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+  CHECK (remove (file_name), "remove \"%s\"", file_name);
+  random_bytes (buf1, sizeof buf1);
+  CHECK (write (fd, buf1, sizeof buf1) > 0, "write \"%s\"", file_name);
+  msg ("seek \"%s\" to 0", file_name);
+  seek (fd, 0);
+  CHECK (read (fd, buf2, sizeof buf2) > 0, "read \"%s\"", file_name);
+  compare_bytes (buf2, buf1, sizeof buf1, 0, file_name);
+  msg ("close \"%s\"", file_name);
+  close (fd);
+}
diff --git a/src/tests/filesys/base/syn-remove.ck b/src/tests/filesys/base/syn-remove.ck
new file mode 100644
index 0000000..16ff11e
--- /dev/null
+++ b/src/tests/filesys/base/syn-remove.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(syn-remove) begin
+(syn-remove) create "deleteme"
+(syn-remove) open "deleteme"
+(syn-remove) remove "deleteme"
+(syn-remove) write "deleteme"
+(syn-remove) seek "deleteme" to 0
+(syn-remove) read "deleteme"
+(syn-remove) close "deleteme"
+(syn-remove) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/syn-write.c b/src/tests/filesys/base/syn-write.c
new file mode 100644
index 0000000..1439862
--- /dev/null
+++ b/src/tests/filesys/base/syn-write.c
@@ -0,0 +1,31 @@
+/* Spawns several child processes to write out different parts of
+   the contents of a file and waits for them to finish.  Then
+   reads back the file and verifies its contents. */
+
+#include <random.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+#include "tests/filesys/base/syn-write.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+char buf1[BUF_SIZE];
+char buf2[BUF_SIZE];
+
+void
+test_main (void) 
+{
+  pid_t children[CHILD_CNT];
+  int fd;
+
+  CHECK (create (file_name, sizeof buf1), "create \"%s\"", file_name);
+
+  exec_children ("child-syn-wrt", children, CHILD_CNT);
+  wait_children (children, CHILD_CNT);
+
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+  CHECK (read (fd, buf1, sizeof buf1) > 0, "read \"%s\"", file_name);
+  random_bytes (buf2, sizeof buf2);
+  compare_bytes (buf1, buf2, sizeof buf1, 0, file_name);
+}
diff --git a/src/tests/filesys/base/syn-write.ck b/src/tests/filesys/base/syn-write.ck
new file mode 100644
index 0000000..629a7a2
--- /dev/null
+++ b/src/tests/filesys/base/syn-write.ck
@@ -0,0 +1,32 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(syn-write) begin
+(syn-write) create "stuff"
+(syn-write) exec child 1 of 10: "child-syn-wrt 0"
+(syn-write) exec child 2 of 10: "child-syn-wrt 1"
+(syn-write) exec child 3 of 10: "child-syn-wrt 2"
+(syn-write) exec child 4 of 10: "child-syn-wrt 3"
+(syn-write) exec child 5 of 10: "child-syn-wrt 4"
+(syn-write) exec child 6 of 10: "child-syn-wrt 5"
+(syn-write) exec child 7 of 10: "child-syn-wrt 6"
+(syn-write) exec child 8 of 10: "child-syn-wrt 7"
+(syn-write) exec child 9 of 10: "child-syn-wrt 8"
+(syn-write) exec child 10 of 10: "child-syn-wrt 9"
+(syn-write) wait for child 1 of 10 returned 0 (expected 0)
+(syn-write) wait for child 2 of 10 returned 1 (expected 1)
+(syn-write) wait for child 3 of 10 returned 2 (expected 2)
+(syn-write) wait for child 4 of 10 returned 3 (expected 3)
+(syn-write) wait for child 5 of 10 returned 4 (expected 4)
+(syn-write) wait for child 6 of 10 returned 5 (expected 5)
+(syn-write) wait for child 7 of 10 returned 6 (expected 6)
+(syn-write) wait for child 8 of 10 returned 7 (expected 7)
+(syn-write) wait for child 9 of 10 returned 8 (expected 8)
+(syn-write) wait for child 10 of 10 returned 9 (expected 9)
+(syn-write) open "stuff"
+(syn-write) read "stuff"
+(syn-write) end
+EOF
+pass;
diff --git a/src/tests/filesys/base/syn-write.h b/src/tests/filesys/base/syn-write.h
new file mode 100644
index 0000000..07a6d5a
--- /dev/null
+++ b/src/tests/filesys/base/syn-write.h
@@ -0,0 +1,9 @@
+#ifndef TESTS_FILESYS_BASE_SYN_WRITE_H
+#define TESTS_FILESYS_BASE_SYN_WRITE_H
+
+#define CHILD_CNT 10
+#define CHUNK_SIZE 512
+#define BUF_SIZE (CHILD_CNT * CHUNK_SIZE)
+static const char file_name[] = "stuff";
+
+#endif /* tests/filesys/base/syn-write.h */
diff --git a/src/tests/filesys/create.inc b/src/tests/filesys/create.inc
new file mode 100644
index 0000000..4baf771
--- /dev/null
+++ b/src/tests/filesys/create.inc
@@ -0,0 +1,15 @@
+/* -*- c -*- */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char buf[TEST_SIZE];
+
+void
+test_main (void) 
+{
+  const char *file_name = "blargle";
+  CHECK (create (file_name, TEST_SIZE), "create \"%s\"", file_name);
+  check_file (file_name, buf, TEST_SIZE);
+}
diff --git a/src/tests/filesys/extended/Make.tests b/src/tests/filesys/extended/Make.tests
new file mode 100644
index 0000000..e03b98d
--- /dev/null
+++ b/src/tests/filesys/extended/Make.tests
@@ -0,0 +1,61 @@
+# -*- makefile -*-
+
+raw_tests = dir-empty-name dir-mk-tree dir-mkdir dir-open		\
+dir-over-file dir-rm-cwd dir-rm-parent dir-rm-root dir-rm-tree		\
+dir-rmdir dir-under-file dir-vine grow-create grow-dir-lg		\
+grow-file-size grow-root-lg grow-root-sm grow-seq-lg grow-seq-sm	\
+grow-sparse grow-tell grow-two-files syn-rw
+
+tests/filesys/extended_TESTS = $(patsubst %,tests/filesys/extended/%,$(raw_tests))
+tests/filesys/extended_EXTRA_GRADES = $(patsubst %,tests/filesys/extended/%-persistence,$(raw_tests))
+
+tests/filesys/extended_PROGS = $(tests/filesys/extended_TESTS) \
+tests/filesys/extended/child-syn-rw tests/filesys/extended/tar
+
+$(foreach prog,$(tests/filesys/extended_PROGS),			\
+	$(eval $(prog)_SRC += $(prog).c tests/lib.c tests/filesys/seq-test.c))
+$(foreach prog,$(tests/filesys/extended_TESTS),		\
+	$(eval $(prog)_SRC += tests/main.c))
+$(foreach prog,$(tests/filesys/extended_TESTS),		\
+	$(eval $(prog)_PUTFILES += tests/filesys/extended/tar))
+# The version of GNU make 3.80 on vine barfs if this is split at
+# the last comma.
+$(foreach test,$(tests/filesys/extended_TESTS),$(eval $(test).output: FILESYSSOURCE = --disk=tmp.dsk))
+
+tests/filesys/extended/dir-mk-tree_SRC += tests/filesys/extended/mk-tree.c
+tests/filesys/extended/dir-rm-tree_SRC += tests/filesys/extended/mk-tree.c
+
+tests/filesys/extended/syn-rw_PUTFILES += tests/filesys/extended/child-syn-rw
+
+tests/filesys/extended/dir-vine.output: TIMEOUT = 150
+
+GETTIMEOUT = 60
+
+GETCMD = pintos -v -k -T $(GETTIMEOUT)
+GETCMD += $(PINTOSOPTS)
+GETCMD += $(SIMULATOR)
+GETCMD += $(FILESYSSOURCE)
+GETCMD += -g fs.tar -a $(TEST).tar
+ifeq ($(filter vm, $(KERNEL_SUBDIRS)), vm)
+GETCMD += --swap-size=4
+endif
+GETCMD += -- -q
+GETCMD += $(KERNELFLAGS)
+GETCMD += run 'tar fs.tar /'
+GETCMD += < /dev/null
+GETCMD += 2> $(TEST)-persistence.errors $(if $(VERBOSE),|tee,>) $(TEST)-persistence.output
+
+tests/filesys/extended/%.output: kernel.bin
+	rm -f tmp.dsk
+	pintos-mkdisk tmp.dsk --filesys-size=2
+	$(TESTCMD)
+	$(GETCMD)
+	rm -f tmp.dsk
+$(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.output: tests/filesys/extended/$(raw_test).output))
+$(foreach raw_test,$(raw_tests),$(eval tests/filesys/extended/$(raw_test)-persistence.result: tests/filesys/extended/$(raw_test).result))
+
+TARS = $(addsuffix .tar,$(tests/filesys/extended_TESTS))
+
+clean::
+	rm -f $(TARS)
+	rm -f tests/filesys/extended/can-rmdir-cwd
diff --git a/src/tests/filesys/extended/Rubric.functionality b/src/tests/filesys/extended/Rubric.functionality
new file mode 100644
index 0000000..91ed6f0
--- /dev/null
+++ b/src/tests/filesys/extended/Rubric.functionality
@@ -0,0 +1,26 @@
+Functionality of extended file system:
+- Test directory support.
+1	dir-mkdir
+3	dir-mk-tree
+
+1	dir-rmdir
+3	dir-rm-tree
+
+5	dir-vine
+
+- Test file growth.
+1	grow-create
+1	grow-seq-sm
+3	grow-seq-lg
+3	grow-sparse
+3	grow-two-files
+1	grow-tell
+1	grow-file-size
+
+- Test directory growth.
+1	grow-dir-lg
+1	grow-root-sm
+1	grow-root-lg
+
+- Test writing from multiple processes.
+5	syn-rw
diff --git a/src/tests/filesys/extended/Rubric.persistence b/src/tests/filesys/extended/Rubric.persistence
new file mode 100644
index 0000000..405620a
--- /dev/null
+++ b/src/tests/filesys/extended/Rubric.persistence
@@ -0,0 +1,24 @@
+Persistence of file system:
+1	dir-empty-name-persistence
+1	dir-mk-tree-persistence
+1	dir-mkdir-persistence
+1	dir-open-persistence
+1	dir-over-file-persistence
+1	dir-rm-cwd-persistence
+1	dir-rm-parent-persistence
+1	dir-rm-root-persistence
+1	dir-rm-tree-persistence
+1	dir-rmdir-persistence
+1	dir-under-file-persistence
+1	dir-vine-persistence
+1	grow-create-persistence
+1	grow-dir-lg-persistence
+1	grow-file-size-persistence
+1	grow-root-lg-persistence
+1	grow-root-sm-persistence
+1	grow-seq-lg-persistence
+1	grow-seq-sm-persistence
+1	grow-sparse-persistence
+1	grow-tell-persistence
+1	grow-two-files-persistence
+1	syn-rw-persistence
diff --git a/src/tests/filesys/extended/Rubric.robustness b/src/tests/filesys/extended/Rubric.robustness
new file mode 100644
index 0000000..fb9f32f
--- /dev/null
+++ b/src/tests/filesys/extended/Rubric.robustness
@@ -0,0 +1,9 @@
+Robustness of file system:
+1	dir-empty-name
+1	dir-open
+1	dir-over-file
+1	dir-under-file
+
+3	dir-rm-cwd
+2	dir-rm-parent
+1	dir-rm-root
diff --git a/src/tests/filesys/extended/child-syn-rw.c b/src/tests/filesys/extended/child-syn-rw.c
new file mode 100644
index 0000000..d456a3a
--- /dev/null
+++ b/src/tests/filesys/extended/child-syn-rw.c
@@ -0,0 +1,52 @@
+/* Child process for syn-rw.
+   Reads from a file created by our parent process, which is
+   growing it.  We loop until we've read the whole file
+   successfully.  Many iterations through the loop will return 0
+   bytes, because the file has not grown in the meantime.  That
+   is, we are "busy waiting" for the file to grow.
+   (This test could be improved by adding a "yield" system call
+   and calling yield whenever we receive a 0-byte read.) */
+
+#include <random.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include "tests/filesys/extended/syn-rw.h"
+#include "tests/lib.h"
+
+static char buf1[BUF_SIZE];
+static char buf2[BUF_SIZE];
+
+int
+main (int argc, const char *argv[]) 
+{
+  int child_idx;
+  int fd;
+  size_t ofs;
+
+  test_name = "child-syn-rw";
+  quiet = true;
+  
+  CHECK (argc == 2, "argc must be 2, actually %d", argc);
+  child_idx = atoi (argv[1]);
+
+  random_init (0);
+  random_bytes (buf1, sizeof buf1);
+
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+  ofs = 0;
+  while (ofs < sizeof buf2)
+    {
+      int bytes_read = read (fd, buf2 + ofs, sizeof buf2 - ofs);
+      CHECK (bytes_read >= -1 && bytes_read <= (int) (sizeof buf2 - ofs),
+             "%zu-byte read on \"%s\" returned invalid value of %d",
+             sizeof buf2 - ofs, file_name, bytes_read);
+      if (bytes_read > 0) 
+        {
+          compare_bytes (buf2 + ofs, buf1 + ofs, bytes_read, ofs, file_name);
+          ofs += bytes_read;
+        }
+    }
+  close (fd);
+
+  return child_idx;
+}
diff --git a/src/tests/filesys/extended/dir-empty-name-persistence.ck b/src/tests/filesys/extended/dir-empty-name-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/src/tests/filesys/extended/dir-empty-name-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({});
+pass;
diff --git a/src/tests/filesys/extended/dir-empty-name.c b/src/tests/filesys/extended/dir-empty-name.c
new file mode 100644
index 0000000..c4859d2
--- /dev/null
+++ b/src/tests/filesys/extended/dir-empty-name.c
@@ -0,0 +1,12 @@
+/* Tries to create a directory named as the empty string,
+   which must return failure. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (!mkdir (""), "mkdir \"\" (must return false)");
+}
diff --git a/src/tests/filesys/extended/dir-empty-name.ck b/src/tests/filesys/extended/dir-empty-name.ck
new file mode 100644
index 0000000..d6c5621
--- /dev/null
+++ b/src/tests/filesys/extended/dir-empty-name.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-empty-name) begin
+(dir-empty-name) mkdir "" (must return false)
+(dir-empty-name) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-mk-tree-persistence.ck b/src/tests/filesys/extended/dir-mk-tree-persistence.ck
new file mode 100644
index 0000000..fb16afd
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mk-tree-persistence.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+my ($tree);
+for my $a (0...3) {
+    for my $b (0...2) {
+	for my $c (0...2) {
+	    for my $d (0...3) {
+		$tree->{$a}{$b}{$c}{$d} = [''];
+	    }
+	}
+    }
+}
+check_archive ($tree);
+pass;
diff --git a/src/tests/filesys/extended/dir-mk-tree.c b/src/tests/filesys/extended/dir-mk-tree.c
new file mode 100644
index 0000000..a714ff3
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mk-tree.c
@@ -0,0 +1,12 @@
+/* Creates directories /0/0/0 through /3/2/2 and creates files in
+   the leaf directories. */
+
+#include "tests/filesys/extended/mk-tree.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  make_tree (4, 3, 3, 4);
+}
+
diff --git a/src/tests/filesys/extended/dir-mk-tree.ck b/src/tests/filesys/extended/dir-mk-tree.ck
new file mode 100644
index 0000000..a8507e2
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mk-tree.ck
@@ -0,0 +1,12 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-mk-tree) begin
+(dir-mk-tree) creating /0/0/0/0 through /3/2/2/3...
+(dir-mk-tree) open "/0/2/0/3"
+(dir-mk-tree) close "/0/2/0/3"
+(dir-mk-tree) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-mkdir-persistence.ck b/src/tests/filesys/extended/dir-mkdir-persistence.ck
new file mode 100644
index 0000000..7682900
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mkdir-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({'a' => {'b' => ["\0" x 512]}});
+pass;
diff --git a/src/tests/filesys/extended/dir-mkdir.c b/src/tests/filesys/extended/dir-mkdir.c
new file mode 100644
index 0000000..994f41c
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mkdir.c
@@ -0,0 +1,15 @@
+/* Tests mkdir(). */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (mkdir ("a"), "mkdir \"a\"");
+  CHECK (create ("a/b", 512), "create \"a/b\"");
+  CHECK (chdir ("a"), "chdir \"a\"");
+  CHECK (open ("b") > 1, "open \"b\"");
+}
+
diff --git a/src/tests/filesys/extended/dir-mkdir.ck b/src/tests/filesys/extended/dir-mkdir.ck
new file mode 100644
index 0000000..4644f80
--- /dev/null
+++ b/src/tests/filesys/extended/dir-mkdir.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-mkdir) begin
+(dir-mkdir) mkdir "a"
+(dir-mkdir) create "a/b"
+(dir-mkdir) chdir "a"
+(dir-mkdir) open "b"
+(dir-mkdir) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-open-persistence.ck b/src/tests/filesys/extended/dir-open-persistence.ck
new file mode 100644
index 0000000..26ff2f1
--- /dev/null
+++ b/src/tests/filesys/extended/dir-open-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"xyzzy" => {}});
+pass;
diff --git a/src/tests/filesys/extended/dir-open.c b/src/tests/filesys/extended/dir-open.c
new file mode 100644
index 0000000..29d18b8
--- /dev/null
+++ b/src/tests/filesys/extended/dir-open.c
@@ -0,0 +1,21 @@
+/* Opens a directory, then tries to write to it, which must
+   fail. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int fd;
+  int retval;
+  
+  CHECK (mkdir ("xyzzy"), "mkdir \"xyzzy\"");
+  CHECK ((fd = open ("xyzzy")) > 1, "open \"xyzzy\"");
+
+  msg ("write \"xyzzy\"");
+  retval = write (fd, "foobar", 6);
+  CHECK (retval == -1,
+         "write \"xyzzy\" (must return -1, actually %d)", retval);
+}
diff --git a/src/tests/filesys/extended/dir-open.ck b/src/tests/filesys/extended/dir-open.ck
new file mode 100644
index 0000000..fccc563
--- /dev/null
+++ b/src/tests/filesys/extended/dir-open.ck
@@ -0,0 +1,20 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(dir-open) begin
+(dir-open) mkdir "xyzzy"
+(dir-open) open "xyzzy"
+(dir-open) write "xyzzy"
+(dir-open) write "xyzzy" (must return -1, actually -1)
+(dir-open) end
+dir-open: exit(0)
+EOF
+(dir-open) begin
+(dir-open) mkdir "xyzzy"
+(dir-open) open "xyzzy"
+(dir-open) write "xyzzy"
+dir-open: exit(-1)
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-over-file-persistence.ck b/src/tests/filesys/extended/dir-over-file-persistence.ck
new file mode 100644
index 0000000..56b4ed2
--- /dev/null
+++ b/src/tests/filesys/extended/dir-over-file-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"abc" => {}});
+pass;
diff --git a/src/tests/filesys/extended/dir-over-file.c b/src/tests/filesys/extended/dir-over-file.c
new file mode 100644
index 0000000..cdd2c62
--- /dev/null
+++ b/src/tests/filesys/extended/dir-over-file.c
@@ -0,0 +1,13 @@
+/* Tries to create a file with the same name as an existing
+   directory, which must return failure. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (mkdir ("abc"), "mkdir \"abc\"");
+  CHECK (!create ("abc", 0), "create \"abc\" (must return false)");
+}
diff --git a/src/tests/filesys/extended/dir-over-file.ck b/src/tests/filesys/extended/dir-over-file.ck
new file mode 100644
index 0000000..aae1c1e
--- /dev/null
+++ b/src/tests/filesys/extended/dir-over-file.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-over-file) begin
+(dir-over-file) mkdir "abc"
+(dir-over-file) create "abc" (must return false)
+(dir-over-file) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-cwd-persistence.ck b/src/tests/filesys/extended/dir-rm-cwd-persistence.ck
new file mode 100644
index 0000000..7533570
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-cwd-persistence.ck
@@ -0,0 +1,8 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+my ($cwd_removable) = read_text_file ("tests/filesys/extended/can-rmdir-cwd");
+$cwd_removable eq 'YES' || $cwd_removable eq 'NO' or die;
+check_archive ($cwd_removable eq 'YES' ? {} : {"a" => {}});
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-cwd.c b/src/tests/filesys/extended/dir-rm-cwd.c
new file mode 100644
index 0000000..78e13de
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-cwd.c
@@ -0,0 +1,75 @@
+/* Tries to remove the current directory, which may succeed or
+   fail.  The requirements in each case are different; refer to
+   the assignment for details. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static int
+wrap_open (const char *name) 
+{
+  static int fds[8], fd_cnt;
+  int fd, i;
+
+  CHECK ((fd = open (name)) > 1, "open \"%s\"", name);
+  for (i = 0; i < fd_cnt; i++)
+    if (fds[i] == fd)
+      fail ("fd returned is not unique");
+  fds[fd_cnt++] = fd;
+  return fd;
+}
+
+void
+test_main (void) 
+{
+  int root_fd, a_fd0;
+  char name[READDIR_MAX_LEN + 1];
+
+  root_fd = wrap_open ("/");
+  CHECK (mkdir ("a"), "mkdir \"a\"");
+
+  a_fd0 = wrap_open ("/a");
+  CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty");
+  CHECK (inumber (root_fd) != inumber (a_fd0),
+         "\"/\" and \"/a\" must have different inumbers");
+
+  CHECK (chdir ("a"), "chdir \"a\"");
+
+  msg ("try to remove \"/a\"");
+  if (remove ("/a"))
+    {
+      msg ("remove successful");
+
+      CHECK (open ("/a") == -1, "open \"/a\" (must fail)");
+      CHECK (open (".") == -1, "open \".\" (must fail)");
+      CHECK (open ("..") == -1, "open \"..\" (must fail)");
+      CHECK (!create ("x", 512), "create \"x\" (must fail)");
+    }
+  else
+    {
+      int a_fd1, a_fd2, a_fd3;
+
+      msg ("remove failed");
+
+      CHECK (!remove ("../a"), "try to remove \"../a\" (must fail)");
+      CHECK (!remove (".././a"), "try to remove \".././a\" (must fail)");
+      CHECK (!remove ("/./a"), "try to remove \"/./a\" (must fail)");
+
+      a_fd1 = wrap_open ("/a");
+      a_fd2 = wrap_open (".");
+      CHECK (inumber (a_fd1) == inumber (a_fd2),
+             "\"/a\" and \".\" must have same inumber");
+      CHECK (inumber (root_fd) != inumber (a_fd1),
+             "\"/\" and \"/a\" must have different inumbers");
+
+      CHECK (chdir ("/a"), "chdir \"/a\"");
+      a_fd3 = wrap_open (".");
+      CHECK (inumber (a_fd3) == inumber (a_fd1),
+             "\".\" must have same inumber as before");
+
+      CHECK (chdir ("/"), "chdir \"/\"");
+      CHECK (!remove ("a"), "try to remove \"a\" (must fail: still open)");
+    }
+  CHECK (!readdir (a_fd0, name), "verify \"/a\" is empty");
+}
diff --git a/src/tests/filesys/extended/dir-rm-cwd.ck b/src/tests/filesys/extended/dir-rm-cwd.ck
new file mode 100644
index 0000000..6fa4739
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-cwd.ck
@@ -0,0 +1,51 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+my ($cwd_removable) = check_expected (IGNORE_EXIT_CODES => 1,
+				      {NO => <<'EOF', YES => <<'EOF'});
+(dir-rm-cwd) begin
+(dir-rm-cwd) open "/"
+(dir-rm-cwd) mkdir "a"
+(dir-rm-cwd) open "/a"
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) "/" and "/a" must have different inumbers
+(dir-rm-cwd) chdir "a"
+(dir-rm-cwd) try to remove "/a"
+(dir-rm-cwd) remove failed
+(dir-rm-cwd) try to remove "../a" (must fail)
+(dir-rm-cwd) try to remove ".././a" (must fail)
+(dir-rm-cwd) try to remove "/./a" (must fail)
+(dir-rm-cwd) open "/a"
+(dir-rm-cwd) open "."
+(dir-rm-cwd) "/a" and "." must have same inumber
+(dir-rm-cwd) "/" and "/a" must have different inumbers
+(dir-rm-cwd) chdir "/a"
+(dir-rm-cwd) open "."
+(dir-rm-cwd) "." must have same inumber as before
+(dir-rm-cwd) chdir "/"
+(dir-rm-cwd) try to remove "a" (must fail: still open)
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) end
+EOF
+(dir-rm-cwd) begin
+(dir-rm-cwd) open "/"
+(dir-rm-cwd) mkdir "a"
+(dir-rm-cwd) open "/a"
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) "/" and "/a" must have different inumbers
+(dir-rm-cwd) chdir "a"
+(dir-rm-cwd) try to remove "/a"
+(dir-rm-cwd) remove successful
+(dir-rm-cwd) open "/a" (must fail)
+(dir-rm-cwd) open "." (must fail)
+(dir-rm-cwd) open ".." (must fail)
+(dir-rm-cwd) create "x" (must fail)
+(dir-rm-cwd) verify "/a" is empty
+(dir-rm-cwd) end
+EOF
+open (CAN_RMDIR_CWD, ">tests/filesys/extended/can-rmdir-cwd")
+  or die "tests/filesys/extended/can-rmdir-cwd: create: $!\n";
+print CAN_RMDIR_CWD "$cwd_removable";
+close (CAN_RMDIR_CWD);
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-parent-persistence.ck b/src/tests/filesys/extended/dir-rm-parent-persistence.ck
new file mode 100644
index 0000000..f30b04a
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-parent-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"a" => {"b" => {}}});
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-parent.c b/src/tests/filesys/extended/dir-rm-parent.c
new file mode 100644
index 0000000..eb43f5b
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-parent.c
@@ -0,0 +1,16 @@
+/* Tries to remove a parent of the current directory.  This must
+   fail, because that directory is non-empty. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (mkdir ("a"), "mkdir \"a\"");
+  CHECK (chdir ("a"), "chdir \"a\"");
+  CHECK (mkdir ("b"), "mkdir \"b\"");
+  CHECK (chdir ("b"), "chdir \"b\"");
+  CHECK (!remove ("/a"), "remove \"/a\" (must fail)");
+}
diff --git a/src/tests/filesys/extended/dir-rm-parent.ck b/src/tests/filesys/extended/dir-rm-parent.ck
new file mode 100644
index 0000000..9fea8f2
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-parent.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-rm-parent) begin
+(dir-rm-parent) mkdir "a"
+(dir-rm-parent) chdir "a"
+(dir-rm-parent) mkdir "b"
+(dir-rm-parent) chdir "b"
+(dir-rm-parent) remove "/a" (must fail)
+(dir-rm-parent) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-root-persistence.ck b/src/tests/filesys/extended/dir-rm-root-persistence.ck
new file mode 100644
index 0000000..6315107
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-root-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"a" => ["\0" x 243]});
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-root.c b/src/tests/filesys/extended/dir-rm-root.c
new file mode 100644
index 0000000..c47f1eb
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-root.c
@@ -0,0 +1,13 @@
+/* Try to remove the root directory.
+   This must fail. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (!remove ("/"), "remove \"/\" (must fail)");
+  CHECK (create ("/a", 243), "create \"/a\"");
+}
diff --git a/src/tests/filesys/extended/dir-rm-root.ck b/src/tests/filesys/extended/dir-rm-root.ck
new file mode 100644
index 0000000..8a69ff3
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-root.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-rm-root) begin
+(dir-rm-root) remove "/" (must fail)
+(dir-rm-root) create "/a"
+(dir-rm-root) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-tree-persistence.ck b/src/tests/filesys/extended/dir-rm-tree-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-tree-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({});
+pass;
diff --git a/src/tests/filesys/extended/dir-rm-tree.c b/src/tests/filesys/extended/dir-rm-tree.c
new file mode 100644
index 0000000..bab41a6
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-tree.c
@@ -0,0 +1,62 @@
+/* Creates directories /0/0/0 through /3/2/2 and files in the
+   leaf directories, then removes them. */
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/filesys/extended/mk-tree.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static void remove_tree (int at, int bt, int ct, int dt);
+
+void
+test_main (void) 
+{
+  make_tree (4, 3, 3, 4);
+  remove_tree (4, 3, 3, 4);
+}
+
+static void do_remove (const char *format, ...) PRINTF_FORMAT (1, 2);
+
+static void
+remove_tree (int at, int bt, int ct, int dt) 
+{
+  char try[128];
+  int a, b, c, d;
+
+  msg ("removing /0/0/0/0 through /%d/%d/%d/%d...",
+       at - 1, bt - 1, ct - 1, dt - 1);
+  quiet = true;
+  for (a = 0; a < at; a++) 
+    {
+      for (b = 0; b < bt; b++) 
+        {
+          for (c = 0; c < ct; c++) 
+            {
+              for (d = 0; d < dt; d++)
+                do_remove ("/%d/%d/%d/%d", a, b, c, d);
+              do_remove ("/%d/%d/%d", a, b, c);
+            }
+          do_remove ("/%d/%d", a, b);
+        }
+      do_remove ("/%d", a);
+    }
+  quiet = false;
+
+  snprintf (try, sizeof (try), "/%d/%d/%d/%d", at - 1, 0, ct - 1, 0);
+  CHECK (open (try) == -1, "open \"%s\" (must return -1)", try);
+}
+
+static void
+do_remove (const char *format, ...) 
+{
+  char name[128];
+  va_list args;
+
+  va_start (args, format);
+  vsnprintf (name, sizeof name, format, args);
+  va_end (args);
+
+  CHECK (remove (name), "remove \"%s\"", name);
+}
diff --git a/src/tests/filesys/extended/dir-rm-tree.ck b/src/tests/filesys/extended/dir-rm-tree.ck
new file mode 100644
index 0000000..587b493
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rm-tree.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-rm-tree) begin
+(dir-rm-tree) creating /0/0/0/0 through /3/2/2/3...
+(dir-rm-tree) open "/0/2/0/3"
+(dir-rm-tree) close "/0/2/0/3"
+(dir-rm-tree) removing /0/0/0/0 through /3/2/2/3...
+(dir-rm-tree) open "/3/0/2/0" (must return -1)
+(dir-rm-tree) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-rmdir-persistence.ck b/src/tests/filesys/extended/dir-rmdir-persistence.ck
new file mode 100644
index 0000000..562c451
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rmdir-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({});
+pass;
diff --git a/src/tests/filesys/extended/dir-rmdir.c b/src/tests/filesys/extended/dir-rmdir.c
new file mode 100644
index 0000000..b3cbc6f
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rmdir.c
@@ -0,0 +1,14 @@
+/* Creates and removes a directory, then makes sure that it's
+   really gone. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (mkdir ("a"), "mkdir \"a\"");
+  CHECK (remove ("a"), "rmdir \"a\"");
+  CHECK (!chdir ("a"), "chdir \"a\" (must return false)");
+}
diff --git a/src/tests/filesys/extended/dir-rmdir.ck b/src/tests/filesys/extended/dir-rmdir.ck
new file mode 100644
index 0000000..e0d8922
--- /dev/null
+++ b/src/tests/filesys/extended/dir-rmdir.ck
@@ -0,0 +1,12 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-rmdir) begin
+(dir-rmdir) mkdir "a"
+(dir-rmdir) rmdir "a"
+(dir-rmdir) chdir "a" (must return false)
+(dir-rmdir) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-under-file-persistence.ck b/src/tests/filesys/extended/dir-under-file-persistence.ck
new file mode 100644
index 0000000..67ca528
--- /dev/null
+++ b/src/tests/filesys/extended/dir-under-file-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"abc" => ['']});
+pass;
diff --git a/src/tests/filesys/extended/dir-under-file.c b/src/tests/filesys/extended/dir-under-file.c
new file mode 100644
index 0000000..973a8b1
--- /dev/null
+++ b/src/tests/filesys/extended/dir-under-file.c
@@ -0,0 +1,13 @@
+/* Tries to create a directory with the same name as an existing
+   file, which must return failure. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (create ("abc", 0), "create \"abc\"");
+  CHECK (!mkdir ("abc"), "mkdir \"abc\" (must return false)");
+}
diff --git a/src/tests/filesys/extended/dir-under-file.ck b/src/tests/filesys/extended/dir-under-file.ck
new file mode 100644
index 0000000..cce23b4
--- /dev/null
+++ b/src/tests/filesys/extended/dir-under-file.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-under-file) begin
+(dir-under-file) create "abc"
+(dir-under-file) mkdir "abc" (must return false)
+(dir-under-file) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/dir-vine-persistence.ck b/src/tests/filesys/extended/dir-vine-persistence.ck
new file mode 100644
index 0000000..698ef01
--- /dev/null
+++ b/src/tests/filesys/extended/dir-vine-persistence.ck
@@ -0,0 +1,37 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+# The archive should look like this:
+#
+# 40642 dir-vine
+# 42479 tar
+#     0 start
+#    11 start/file0
+#     0 start/dir0
+#    11 start/dir0/file1
+#     0 start/dir0/dir1
+#    11 start/dir0/dir1/file2
+#     0 start/dir0/dir1/dir2
+#    11 start/dir0/dir1/dir2/file3
+#     0 start/dir0/dir1/dir2/dir3
+#    11 start/dir0/dir1/dir2/dir3/file4
+#     0 start/dir0/dir1/dir2/dir3/dir4
+#    11 start/dir0/dir1/dir2/dir3/dir4/file5
+#     0 start/dir0/dir1/dir2/dir3/dir4/dir5
+#    11 start/dir0/dir1/dir2/dir3/dir4/dir5/file6
+#     0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6
+#    11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/file7
+#     0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7
+#    11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/file8
+#     0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8
+#    11 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/file9
+#     0 start/dir0/dir1/dir2/dir3/dir4/dir5/dir6/dir7/dir8/dir9
+my ($dir) = {};
+my ($root) = {"start" => $dir};
+for (my ($i) = 0; $i < 10; $i++) {
+    $dir->{"file$i"} = ["contents $i\n"];
+    $dir = $dir->{"dir$i"} = {};
+}
+check_archive ($root);
+pass;
diff --git a/src/tests/filesys/extended/dir-vine.c b/src/tests/filesys/extended/dir-vine.c
new file mode 100644
index 0000000..8a31c38
--- /dev/null
+++ b/src/tests/filesys/extended/dir-vine.c
@@ -0,0 +1,85 @@
+/* Create a very deep "vine" of directories: /dir0/dir1/dir2/...
+   and an ordinary file in each of them, until we fill up the
+   disk.
+   
+   Then delete most of them, for two reasons.  First, "tar"
+   limits file names to 100 characters (which could be extended
+   to 256 without much trouble).  Second, a full disk has no room
+   for the tar archive. */
+
+#include <string.h>
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int i;
+
+  msg ("creating many levels of files and directories...");
+  quiet = true;
+  CHECK (mkdir ("start"), "mkdir \"start\"");
+  CHECK (chdir ("start"), "chdir \"start\"");
+  for (i = 0; ; i++) 
+    {
+      char name[3][READDIR_MAX_LEN + 1];
+      char file_name[16], dir_name[16];
+      char contents[128];
+      int fd;
+
+      /* Create file. */
+      snprintf (file_name, sizeof file_name, "file%d", i);
+      if (!create (file_name, 0))
+        break;
+      CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+      snprintf (contents, sizeof contents, "contents %d\n", i);
+      if (write (fd, contents, strlen (contents)) != (int) strlen (contents)) 
+        {
+          CHECK (remove (file_name), "remove \"%s\"", file_name);
+          close (fd);
+          break;
+        }
+      close (fd);
+      
+      /* Create directory. */
+      snprintf (dir_name, sizeof dir_name, "dir%d", i);
+      if (!mkdir (dir_name)) 
+        {
+          CHECK (remove (file_name), "remove \"%s\"", file_name);
+          break; 
+        }
+
+      /* Check for file and directory. */
+      CHECK ((fd = open (".")) > 1, "open \".\"");
+      CHECK (readdir (fd, name[0]), "readdir \".\"");
+      CHECK (readdir (fd, name[1]), "readdir \".\"");
+      CHECK (!readdir (fd, name[2]), "readdir \".\" (should fail)");
+      CHECK ((!strcmp (name[0], dir_name) && !strcmp (name[1], file_name))
+             || (!strcmp (name[1], dir_name) && !strcmp (name[0], file_name)),
+             "names should be \"%s\" and \"%s\", "
+             "actually \"%s\" and \"%s\"",
+             file_name, dir_name, name[0], name[1]);
+      close (fd);
+
+      /* Descend into directory. */
+      CHECK (chdir (dir_name), "chdir \"%s\"", dir_name);
+    }
+  CHECK (i > 200, "created files and directories only to level %d", i);
+  quiet = false;
+
+  msg ("removing all but top 10 levels of files and directories...");
+  quiet = true;
+  while (i-- > 10) 
+    {
+      char file_name[16], dir_name[16];
+
+      snprintf (file_name, sizeof file_name, "file%d", i);
+      snprintf (dir_name, sizeof dir_name, "dir%d", i);
+      CHECK (chdir (".."), "chdir \"..\"");
+      CHECK (remove (dir_name), "remove \"%s\"", dir_name);
+      CHECK (remove (file_name), "remove \"%s\"", file_name);
+    }
+  quiet = false;
+}
diff --git a/src/tests/filesys/extended/dir-vine.ck b/src/tests/filesys/extended/dir-vine.ck
new file mode 100644
index 0000000..db452b0
--- /dev/null
+++ b/src/tests/filesys/extended/dir-vine.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(dir-vine) begin
+(dir-vine) creating many levels of files and directories...
+(dir-vine) removing all but top 10 levels of files and directories...
+(dir-vine) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-create-persistence.ck b/src/tests/filesys/extended/grow-create-persistence.ck
new file mode 100644
index 0000000..bbcb24f
--- /dev/null
+++ b/src/tests/filesys/extended/grow-create-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"blargle" => ['']});
+pass;
diff --git a/src/tests/filesys/extended/grow-create.c b/src/tests/filesys/extended/grow-create.c
new file mode 100644
index 0000000..9ccc4ea
--- /dev/null
+++ b/src/tests/filesys/extended/grow-create.c
@@ -0,0 +1,4 @@
+/* Create a file of size 0. */
+
+#define TEST_SIZE 0
+#include "tests/filesys/create.inc"
diff --git a/src/tests/filesys/extended/grow-create.ck b/src/tests/filesys/extended/grow-create.ck
new file mode 100644
index 0000000..b2e69d1
--- /dev/null
+++ b/src/tests/filesys/extended/grow-create.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-create) begin
+(grow-create) create "blargle"
+(grow-create) open "blargle" for verification
+(grow-create) verified contents of "blargle"
+(grow-create) close "blargle"
+(grow-create) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-dir-lg-persistence.ck b/src/tests/filesys/extended/grow-dir-lg-persistence.ck
new file mode 100644
index 0000000..989a322
--- /dev/null
+++ b/src/tests/filesys/extended/grow-dir-lg-persistence.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+my ($fs);
+$fs->{'x'}{"file$_"} = [random_bytes (512)] foreach 0...49;
+check_archive ($fs);
+pass;
diff --git a/src/tests/filesys/extended/grow-dir-lg.c b/src/tests/filesys/extended/grow-dir-lg.c
new file mode 100644
index 0000000..20a194b
--- /dev/null
+++ b/src/tests/filesys/extended/grow-dir-lg.c
@@ -0,0 +1,6 @@
+/* Creates a directory,
+   then creates 50 files in that directory. */
+
+#define FILE_CNT 50
+#define DIRECTORY "/x"
+#include "tests/filesys/extended/grow-dir.inc"
diff --git a/src/tests/filesys/extended/grow-dir-lg.ck b/src/tests/filesys/extended/grow-dir-lg.ck
new file mode 100644
index 0000000..ec58bd3
--- /dev/null
+++ b/src/tests/filesys/extended/grow-dir-lg.ck
@@ -0,0 +1,61 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-dir-lg) begin
+(grow-dir-lg) mkdir /x
+(grow-dir-lg) creating and checking "/x/file0"
+(grow-dir-lg) creating and checking "/x/file1"
+(grow-dir-lg) creating and checking "/x/file2"
+(grow-dir-lg) creating and checking "/x/file3"
+(grow-dir-lg) creating and checking "/x/file4"
+(grow-dir-lg) creating and checking "/x/file5"
+(grow-dir-lg) creating and checking "/x/file6"
+(grow-dir-lg) creating and checking "/x/file7"
+(grow-dir-lg) creating and checking "/x/file8"
+(grow-dir-lg) creating and checking "/x/file9"
+(grow-dir-lg) creating and checking "/x/file10"
+(grow-dir-lg) creating and checking "/x/file11"
+(grow-dir-lg) creating and checking "/x/file12"
+(grow-dir-lg) creating and checking "/x/file13"
+(grow-dir-lg) creating and checking "/x/file14"
+(grow-dir-lg) creating and checking "/x/file15"
+(grow-dir-lg) creating and checking "/x/file16"
+(grow-dir-lg) creating and checking "/x/file17"
+(grow-dir-lg) creating and checking "/x/file18"
+(grow-dir-lg) creating and checking "/x/file19"
+(grow-dir-lg) creating and checking "/x/file20"
+(grow-dir-lg) creating and checking "/x/file21"
+(grow-dir-lg) creating and checking "/x/file22"
+(grow-dir-lg) creating and checking "/x/file23"
+(grow-dir-lg) creating and checking "/x/file24"
+(grow-dir-lg) creating and checking "/x/file25"
+(grow-dir-lg) creating and checking "/x/file26"
+(grow-dir-lg) creating and checking "/x/file27"
+(grow-dir-lg) creating and checking "/x/file28"
+(grow-dir-lg) creating and checking "/x/file29"
+(grow-dir-lg) creating and checking "/x/file30"
+(grow-dir-lg) creating and checking "/x/file31"
+(grow-dir-lg) creating and checking "/x/file32"
+(grow-dir-lg) creating and checking "/x/file33"
+(grow-dir-lg) creating and checking "/x/file34"
+(grow-dir-lg) creating and checking "/x/file35"
+(grow-dir-lg) creating and checking "/x/file36"
+(grow-dir-lg) creating and checking "/x/file37"
+(grow-dir-lg) creating and checking "/x/file38"
+(grow-dir-lg) creating and checking "/x/file39"
+(grow-dir-lg) creating and checking "/x/file40"
+(grow-dir-lg) creating and checking "/x/file41"
+(grow-dir-lg) creating and checking "/x/file42"
+(grow-dir-lg) creating and checking "/x/file43"
+(grow-dir-lg) creating and checking "/x/file44"
+(grow-dir-lg) creating and checking "/x/file45"
+(grow-dir-lg) creating and checking "/x/file46"
+(grow-dir-lg) creating and checking "/x/file47"
+(grow-dir-lg) creating and checking "/x/file48"
+(grow-dir-lg) creating and checking "/x/file49"
+(grow-dir-lg) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-dir.inc b/src/tests/filesys/extended/grow-dir.inc
new file mode 100644
index 0000000..bee0ba0
--- /dev/null
+++ b/src/tests/filesys/extended/grow-dir.inc
@@ -0,0 +1,41 @@
+/* -*- c -*- */
+
+#include <syscall.h>
+#include <stdio.h>
+#include "tests/filesys/seq-test.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char buf[512];
+
+static size_t
+return_block_size (void) 
+{
+  return sizeof buf;
+}
+
+void
+test_main (void) 
+{
+  size_t i;
+  
+#ifdef DIRECTORY
+  CHECK (mkdir (DIRECTORY), "mkdir %s", DIRECTORY);
+#define DIR_PREFIX DIRECTORY "/"
+#else
+#define DIR_PREFIX ""
+#endif
+  for (i = 0; i < FILE_CNT; i++) 
+    {
+      char file_name[128];
+      snprintf (file_name, sizeof file_name, "%sfile%zu", DIR_PREFIX, i);
+
+      msg ("creating and checking \"%s\"", file_name);
+
+      quiet = true;
+      seq_test (file_name,
+                buf, sizeof buf, sizeof buf,
+                return_block_size, NULL); 
+      quiet = false;
+    }
+}
diff --git a/src/tests/filesys/extended/grow-file-size-persistence.ck b/src/tests/filesys/extended/grow-file-size-persistence.ck
new file mode 100644
index 0000000..150f383
--- /dev/null
+++ b/src/tests/filesys/extended/grow-file-size-persistence.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_archive ({"testfile" => [random_bytes (2134)]});
+pass;
diff --git a/src/tests/filesys/extended/grow-file-size.c b/src/tests/filesys/extended/grow-file-size.c
new file mode 100644
index 0000000..3ce8588
--- /dev/null
+++ b/src/tests/filesys/extended/grow-file-size.c
@@ -0,0 +1,33 @@
+/* Grows a file from 0 bytes to 2,134 bytes, 37 bytes at a time,
+   and checks that the file's size is reported correctly at each
+   step. */
+
+#include <syscall.h>
+#include "tests/filesys/seq-test.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char buf[2134];
+
+static size_t
+return_block_size (void) 
+{
+  return 37;
+}
+
+static void
+check_file_size (int fd, long ofs) 
+{
+  long size = filesize (fd);
+  if (size != ofs)
+    fail ("filesize not updated properly: should be %ld, actually %ld",
+          ofs, size);
+}
+
+void
+test_main (void) 
+{
+  seq_test ("testfile",
+            buf, sizeof buf, 0,
+            return_block_size, check_file_size);
+}
diff --git a/src/tests/filesys/extended/grow-file-size.ck b/src/tests/filesys/extended/grow-file-size.ck
new file mode 100644
index 0000000..d81feff
--- /dev/null
+++ b/src/tests/filesys/extended/grow-file-size.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-file-size) begin
+(grow-file-size) create "testfile"
+(grow-file-size) open "testfile"
+(grow-file-size) writing "testfile"
+(grow-file-size) close "testfile"
+(grow-file-size) open "testfile" for verification
+(grow-file-size) verified contents of "testfile"
+(grow-file-size) close "testfile"
+(grow-file-size) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-root-lg-persistence.ck b/src/tests/filesys/extended/grow-root-lg-persistence.ck
new file mode 100644
index 0000000..1692f46
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-lg-persistence.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+my ($fs);
+$fs->{"file$_"} = [random_bytes (512)] foreach 0...49;
+check_archive ($fs);
+pass;
diff --git a/src/tests/filesys/extended/grow-root-lg.c b/src/tests/filesys/extended/grow-root-lg.c
new file mode 100644
index 0000000..d8d6c09
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-lg.c
@@ -0,0 +1,4 @@
+/* Creates 50 files in the root directory. */
+
+#define FILE_CNT 50
+#include "tests/filesys/extended/grow-dir.inc"
diff --git a/src/tests/filesys/extended/grow-root-lg.ck b/src/tests/filesys/extended/grow-root-lg.ck
new file mode 100644
index 0000000..b174bc9
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-lg.ck
@@ -0,0 +1,60 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-root-lg) begin
+(grow-root-lg) creating and checking "file0"
+(grow-root-lg) creating and checking "file1"
+(grow-root-lg) creating and checking "file2"
+(grow-root-lg) creating and checking "file3"
+(grow-root-lg) creating and checking "file4"
+(grow-root-lg) creating and checking "file5"
+(grow-root-lg) creating and checking "file6"
+(grow-root-lg) creating and checking "file7"
+(grow-root-lg) creating and checking "file8"
+(grow-root-lg) creating and checking "file9"
+(grow-root-lg) creating and checking "file10"
+(grow-root-lg) creating and checking "file11"
+(grow-root-lg) creating and checking "file12"
+(grow-root-lg) creating and checking "file13"
+(grow-root-lg) creating and checking "file14"
+(grow-root-lg) creating and checking "file15"
+(grow-root-lg) creating and checking "file16"
+(grow-root-lg) creating and checking "file17"
+(grow-root-lg) creating and checking "file18"
+(grow-root-lg) creating and checking "file19"
+(grow-root-lg) creating and checking "file20"
+(grow-root-lg) creating and checking "file21"
+(grow-root-lg) creating and checking "file22"
+(grow-root-lg) creating and checking "file23"
+(grow-root-lg) creating and checking "file24"
+(grow-root-lg) creating and checking "file25"
+(grow-root-lg) creating and checking "file26"
+(grow-root-lg) creating and checking "file27"
+(grow-root-lg) creating and checking "file28"
+(grow-root-lg) creating and checking "file29"
+(grow-root-lg) creating and checking "file30"
+(grow-root-lg) creating and checking "file31"
+(grow-root-lg) creating and checking "file32"
+(grow-root-lg) creating and checking "file33"
+(grow-root-lg) creating and checking "file34"
+(grow-root-lg) creating and checking "file35"
+(grow-root-lg) creating and checking "file36"
+(grow-root-lg) creating and checking "file37"
+(grow-root-lg) creating and checking "file38"
+(grow-root-lg) creating and checking "file39"
+(grow-root-lg) creating and checking "file40"
+(grow-root-lg) creating and checking "file41"
+(grow-root-lg) creating and checking "file42"
+(grow-root-lg) creating and checking "file43"
+(grow-root-lg) creating and checking "file44"
+(grow-root-lg) creating and checking "file45"
+(grow-root-lg) creating and checking "file46"
+(grow-root-lg) creating and checking "file47"
+(grow-root-lg) creating and checking "file48"
+(grow-root-lg) creating and checking "file49"
+(grow-root-lg) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-root-sm-persistence.ck b/src/tests/filesys/extended/grow-root-sm-persistence.ck
new file mode 100644
index 0000000..2b0b8ab
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-sm-persistence.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+my ($fs);
+$fs->{"file$_"} = [random_bytes (512)] foreach 0...19;
+check_archive ($fs);
+pass;
diff --git a/src/tests/filesys/extended/grow-root-sm.c b/src/tests/filesys/extended/grow-root-sm.c
new file mode 100644
index 0000000..ee375d5
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-sm.c
@@ -0,0 +1,4 @@
+/* Creates 20 files in the root directory. */
+
+#define FILE_CNT 20
+#include "tests/filesys/extended/grow-dir.inc"
diff --git a/src/tests/filesys/extended/grow-root-sm.ck b/src/tests/filesys/extended/grow-root-sm.ck
new file mode 100644
index 0000000..1aac7e9
--- /dev/null
+++ b/src/tests/filesys/extended/grow-root-sm.ck
@@ -0,0 +1,30 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-root-sm) begin
+(grow-root-sm) creating and checking "file0"
+(grow-root-sm) creating and checking "file1"
+(grow-root-sm) creating and checking "file2"
+(grow-root-sm) creating and checking "file3"
+(grow-root-sm) creating and checking "file4"
+(grow-root-sm) creating and checking "file5"
+(grow-root-sm) creating and checking "file6"
+(grow-root-sm) creating and checking "file7"
+(grow-root-sm) creating and checking "file8"
+(grow-root-sm) creating and checking "file9"
+(grow-root-sm) creating and checking "file10"
+(grow-root-sm) creating and checking "file11"
+(grow-root-sm) creating and checking "file12"
+(grow-root-sm) creating and checking "file13"
+(grow-root-sm) creating and checking "file14"
+(grow-root-sm) creating and checking "file15"
+(grow-root-sm) creating and checking "file16"
+(grow-root-sm) creating and checking "file17"
+(grow-root-sm) creating and checking "file18"
+(grow-root-sm) creating and checking "file19"
+(grow-root-sm) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-seq-lg-persistence.ck b/src/tests/filesys/extended/grow-seq-lg-persistence.ck
new file mode 100644
index 0000000..41aaae0
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-lg-persistence.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_archive ({"testme" => [random_bytes (72943)]});
+pass;
diff --git a/src/tests/filesys/extended/grow-seq-lg.c b/src/tests/filesys/extended/grow-seq-lg.c
new file mode 100644
index 0000000..3108d17
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-lg.c
@@ -0,0 +1,5 @@
+/* Grows a file from 0 bytes to 72,943 bytes, 1,234 bytes at a
+   time. */
+
+#define TEST_SIZE 72943
+#include "tests/filesys/extended/grow-seq.inc"
diff --git a/src/tests/filesys/extended/grow-seq-lg.ck b/src/tests/filesys/extended/grow-seq-lg.ck
new file mode 100644
index 0000000..90fcd8c
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-lg.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-seq-lg) begin
+(grow-seq-lg) create "testme"
+(grow-seq-lg) open "testme"
+(grow-seq-lg) writing "testme"
+(grow-seq-lg) close "testme"
+(grow-seq-lg) open "testme" for verification
+(grow-seq-lg) verified contents of "testme"
+(grow-seq-lg) close "testme"
+(grow-seq-lg) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-seq-sm-persistence.ck b/src/tests/filesys/extended/grow-seq-sm-persistence.ck
new file mode 100644
index 0000000..6cb0bd8
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-sm-persistence.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_archive ({"testme" => [random_bytes (5678)]});
+pass;
diff --git a/src/tests/filesys/extended/grow-seq-sm.c b/src/tests/filesys/extended/grow-seq-sm.c
new file mode 100644
index 0000000..3656e2e
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-sm.c
@@ -0,0 +1,5 @@
+/* Grows a file from 0 bytes to 5,678 bytes, 1,234 bytes at a
+   time. */
+
+#define TEST_SIZE 5678
+#include "tests/filesys/extended/grow-seq.inc"
diff --git a/src/tests/filesys/extended/grow-seq-sm.ck b/src/tests/filesys/extended/grow-seq-sm.ck
new file mode 100644
index 0000000..5cf4518
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq-sm.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-seq-sm) begin
+(grow-seq-sm) create "testme"
+(grow-seq-sm) open "testme"
+(grow-seq-sm) writing "testme"
+(grow-seq-sm) close "testme"
+(grow-seq-sm) open "testme" for verification
+(grow-seq-sm) verified contents of "testme"
+(grow-seq-sm) close "testme"
+(grow-seq-sm) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-seq.inc b/src/tests/filesys/extended/grow-seq.inc
new file mode 100644
index 0000000..1b7710c
--- /dev/null
+++ b/src/tests/filesys/extended/grow-seq.inc
@@ -0,0 +1,20 @@
+/* -*- c -*- */
+
+#include "tests/filesys/seq-test.h"
+#include "tests/main.h"
+
+static char buf[TEST_SIZE];
+
+static size_t
+return_block_size (void) 
+{
+  return 1234;
+}
+
+void
+test_main (void) 
+{
+  seq_test ("testme",
+            buf, sizeof buf, 0,
+            return_block_size, NULL);
+}
diff --git a/src/tests/filesys/extended/grow-sparse-persistence.ck b/src/tests/filesys/extended/grow-sparse-persistence.ck
new file mode 100644
index 0000000..3f06a5b
--- /dev/null
+++ b/src/tests/filesys/extended/grow-sparse-persistence.ck
@@ -0,0 +1,6 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_archive ({"testfile" => ["\0" x 76543]});
+pass;
diff --git a/src/tests/filesys/extended/grow-sparse.c b/src/tests/filesys/extended/grow-sparse.c
new file mode 100644
index 0000000..6eab210
--- /dev/null
+++ b/src/tests/filesys/extended/grow-sparse.c
@@ -0,0 +1,25 @@
+/* Tests that seeking past the end of a file and writing will
+   properly zero out the region in between. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char buf[76543];
+
+void
+test_main (void) 
+{
+  const char *file_name = "testfile";
+  char zero = 0;
+  int fd;
+  
+  CHECK (create (file_name, 0), "create \"%s\"", file_name);
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+  msg ("seek \"%s\"", file_name);
+  seek (fd, sizeof buf - 1);
+  CHECK (write (fd, &zero, 1) > 0, "write \"%s\"", file_name);
+  msg ("close \"%s\"", file_name);
+  close (fd);
+  check_file (file_name, buf, sizeof buf);
+}
diff --git a/src/tests/filesys/extended/grow-sparse.ck b/src/tests/filesys/extended/grow-sparse.ck
new file mode 100644
index 0000000..379ba2c
--- /dev/null
+++ b/src/tests/filesys/extended/grow-sparse.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-sparse) begin
+(grow-sparse) create "testfile"
+(grow-sparse) open "testfile"
+(grow-sparse) seek "testfile"
+(grow-sparse) write "testfile"
+(grow-sparse) close "testfile"
+(grow-sparse) open "testfile" for verification
+(grow-sparse) verified contents of "testfile"
+(grow-sparse) close "testfile"
+(grow-sparse) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-tell-persistence.ck b/src/tests/filesys/extended/grow-tell-persistence.ck
new file mode 100644
index 0000000..d93a422
--- /dev/null
+++ b/src/tests/filesys/extended/grow-tell-persistence.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_archive ({"foobar" => [random_bytes (2134)]});
+pass;
diff --git a/src/tests/filesys/extended/grow-tell.c b/src/tests/filesys/extended/grow-tell.c
new file mode 100644
index 0000000..5f5da5b
--- /dev/null
+++ b/src/tests/filesys/extended/grow-tell.c
@@ -0,0 +1,32 @@
+/* Checks that growing a file updates the file position
+   correctly. */
+
+#include <syscall.h>
+#include "tests/filesys/seq-test.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char buf[2134];
+
+static size_t
+return_block_size (void) 
+{
+  return 37;
+}
+
+static void
+check_tell (int fd, long ofs) 
+{
+  long pos = tell (fd);
+  if (pos != ofs)
+    fail ("file position not updated properly: should be %ld, actually %ld",
+          ofs, pos);
+}
+
+void
+test_main (void) 
+{
+  seq_test ("foobar",
+            buf, sizeof buf, 0,
+            return_block_size, check_tell);
+}
diff --git a/src/tests/filesys/extended/grow-tell.ck b/src/tests/filesys/extended/grow-tell.ck
new file mode 100644
index 0000000..fe94707
--- /dev/null
+++ b/src/tests/filesys/extended/grow-tell.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-tell) begin
+(grow-tell) create "foobar"
+(grow-tell) open "foobar"
+(grow-tell) writing "foobar"
+(grow-tell) close "foobar"
+(grow-tell) open "foobar" for verification
+(grow-tell) verified contents of "foobar"
+(grow-tell) close "foobar"
+(grow-tell) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/grow-two-files-persistence.ck b/src/tests/filesys/extended/grow-two-files-persistence.ck
new file mode 100644
index 0000000..1c4ced1
--- /dev/null
+++ b/src/tests/filesys/extended/grow-two-files-persistence.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+my ($a) = random_bytes (8143);
+my ($b) = random_bytes (8143);
+check_archive ({"a" => [$a], "b" => [$b]});
+pass;
diff --git a/src/tests/filesys/extended/grow-two-files.c b/src/tests/filesys/extended/grow-two-files.c
new file mode 100644
index 0000000..6a8fb1c
--- /dev/null
+++ b/src/tests/filesys/extended/grow-two-files.c
@@ -0,0 +1,62 @@
+/* Grows two files in parallel and checks that their contents are
+   correct. */
+
+#include <random.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define FILE_SIZE 8143
+static char buf_a[FILE_SIZE];
+static char buf_b[FILE_SIZE];
+
+static void
+write_some_bytes (const char *file_name, int fd, const char *buf, size_t *ofs) 
+{
+  if (*ofs < FILE_SIZE) 
+    {
+      size_t block_size = random_ulong () % (FILE_SIZE / 8) + 1;
+      size_t ret_val;
+      if (block_size > FILE_SIZE - *ofs)
+        block_size = FILE_SIZE - *ofs;
+
+      ret_val = write (fd, buf + *ofs, block_size);
+      if (ret_val != block_size)
+        fail ("write %zu bytes at offset %zu in \"%s\" returned %zu",
+              block_size, *ofs, file_name, ret_val);
+      *ofs += block_size;
+    }
+}
+
+void
+test_main (void) 
+{
+  int fd_a, fd_b;
+  size_t ofs_a = 0, ofs_b = 0;
+
+  random_init (0);
+  random_bytes (buf_a, sizeof buf_a);
+  random_bytes (buf_b, sizeof buf_b);
+
+  CHECK (create ("a", 0), "create \"a\"");
+  CHECK (create ("b", 0), "create \"b\"");
+
+  CHECK ((fd_a = open ("a")) > 1, "open \"a\"");
+  CHECK ((fd_b = open ("b")) > 1, "open \"b\"");
+
+  msg ("write \"a\" and \"b\" alternately");
+  while (ofs_a < FILE_SIZE || ofs_b < FILE_SIZE) 
+    {
+      write_some_bytes ("a", fd_a, buf_a, &ofs_a);
+      write_some_bytes ("b", fd_b, buf_b, &ofs_b);
+    }
+
+  msg ("close \"a\"");
+  close (fd_a);
+
+  msg ("close \"b\"");
+  close (fd_b);
+
+  check_file ("a", buf_a, FILE_SIZE);
+  check_file ("b", buf_b, FILE_SIZE);
+}
diff --git a/src/tests/filesys/extended/grow-two-files.ck b/src/tests/filesys/extended/grow-two-files.ck
new file mode 100644
index 0000000..b5e754a
--- /dev/null
+++ b/src/tests/filesys/extended/grow-two-files.ck
@@ -0,0 +1,23 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(grow-two-files) begin
+(grow-two-files) create "a"
+(grow-two-files) create "b"
+(grow-two-files) open "a"
+(grow-two-files) open "b"
+(grow-two-files) write "a" and "b" alternately
+(grow-two-files) close "a"
+(grow-two-files) close "b"
+(grow-two-files) open "a" for verification
+(grow-two-files) verified contents of "a"
+(grow-two-files) close "a"
+(grow-two-files) open "b" for verification
+(grow-two-files) verified contents of "b"
+(grow-two-files) close "b"
+(grow-two-files) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/mk-tree.c b/src/tests/filesys/extended/mk-tree.c
new file mode 100644
index 0000000..a36bb88
--- /dev/null
+++ b/src/tests/filesys/extended/mk-tree.c
@@ -0,0 +1,67 @@
+/* Library function for creating a tree of directories. */
+
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/filesys/extended/mk-tree.h"
+#include "tests/lib.h"
+
+static void do_mkdir (const char *format, ...) PRINTF_FORMAT (1, 2);
+static void do_touch (const char *format, ...) PRINTF_FORMAT (1, 2);
+
+void
+make_tree (int at, int bt, int ct, int dt) 
+{
+  char try[128];
+  int a, b, c, d;
+  int fd;
+
+  msg ("creating /0/0/0/0 through /%d/%d/%d/%d...",
+       at - 1, bt - 1, ct - 1, dt - 1);
+  quiet = true;
+  for (a = 0; a < at; a++) 
+    {
+      do_mkdir ("/%d", a);
+      for (b = 0; b < bt; b++) 
+        {
+          do_mkdir ("/%d/%d", a, b);
+          for (c = 0; c < ct; c++) 
+            {
+              do_mkdir ("/%d/%d/%d", a, b, c);
+              for (d = 0; d < dt; d++)
+                do_touch ("/%d/%d/%d/%d", a, b, c, d);
+            }
+        }
+    }
+  quiet = false;
+
+  snprintf (try, sizeof try, "/%d/%d/%d/%d", 0, bt - 1, 0, dt - 1);
+  CHECK ((fd = open (try)) > 1, "open \"%s\"", try);
+  msg ("close \"%s\"", try);
+  close (fd);
+}
+
+static void
+do_mkdir (const char *format, ...) 
+{
+  char dir[128];
+  va_list args;
+
+  va_start (args, format);
+  vsnprintf (dir, sizeof dir, format, args);
+  va_end (args);
+
+  CHECK (mkdir (dir), "mkdir \"%s\"", dir);
+}
+
+static void
+do_touch (const char *format, ...)
+{
+  char file[128];
+  va_list args;
+
+  va_start (args, format);
+  vsnprintf (file, sizeof file, format, args);
+  va_end (args);
+
+  CHECK (create (file, 0), "create \"%s\"", file);
+}
diff --git a/src/tests/filesys/extended/mk-tree.h b/src/tests/filesys/extended/mk-tree.h
new file mode 100644
index 0000000..df0d5a6
--- /dev/null
+++ b/src/tests/filesys/extended/mk-tree.h
@@ -0,0 +1,6 @@
+#ifndef TESTS_FILESYS_EXTENDED_MK_TREE_H
+#define TESTS_FILESYS_EXTENDED_MK_TREE_H
+
+void make_tree (int at, int bt, int ct, int dt);
+
+#endif /* tests/filesys/extended/mk-tree.h */
diff --git a/src/tests/filesys/extended/syn-rw-persistence.ck b/src/tests/filesys/extended/syn-rw-persistence.ck
new file mode 100644
index 0000000..62d57ee
--- /dev/null
+++ b/src/tests/filesys/extended/syn-rw-persistence.ck
@@ -0,0 +1,8 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_archive ({"child-syn-rw" => "tests/filesys/extended/child-syn-rw",
+		"logfile" => [random_bytes (8 * 512)]});
+pass;
diff --git a/src/tests/filesys/extended/syn-rw.c b/src/tests/filesys/extended/syn-rw.c
new file mode 100644
index 0000000..657dfb5
--- /dev/null
+++ b/src/tests/filesys/extended/syn-rw.c
@@ -0,0 +1,35 @@
+/* Grows a file in chunks while subprocesses read the growing
+   file. */
+
+#include <random.h>
+#include <syscall.h>
+#include "tests/filesys/extended/syn-rw.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+char buf[BUF_SIZE];
+
+#define CHILD_CNT 4
+
+void
+test_main (void) 
+{
+  pid_t children[CHILD_CNT];
+  size_t ofs;
+  int fd;
+
+  CHECK (create (file_name, 0), "create \"%s\"", file_name);
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+
+  exec_children ("child-syn-rw", children, CHILD_CNT);
+
+  random_bytes (buf, sizeof buf);
+  quiet = true;
+  for (ofs = 0; ofs < BUF_SIZE; ofs += CHUNK_SIZE)
+    CHECK (write (fd, buf + ofs, CHUNK_SIZE) > 0,
+           "write %d bytes at offset %zu in \"%s\"",
+           (int) CHUNK_SIZE, ofs, file_name);
+  quiet = false;
+
+  wait_children (children, CHILD_CNT);
+}
diff --git a/src/tests/filesys/extended/syn-rw.ck b/src/tests/filesys/extended/syn-rw.ck
new file mode 100644
index 0000000..ac82aa8
--- /dev/null
+++ b/src/tests/filesys/extended/syn-rw.ck
@@ -0,0 +1,20 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::random;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(syn-rw) begin
+(syn-rw) create "logfile"
+(syn-rw) open "logfile"
+(syn-rw) exec child 1 of 4: "child-syn-rw 0"
+(syn-rw) exec child 2 of 4: "child-syn-rw 1"
+(syn-rw) exec child 3 of 4: "child-syn-rw 2"
+(syn-rw) exec child 4 of 4: "child-syn-rw 3"
+(syn-rw) wait for child 1 of 4 returned 0 (expected 0)
+(syn-rw) wait for child 2 of 4 returned 1 (expected 1)
+(syn-rw) wait for child 3 of 4 returned 2 (expected 2)
+(syn-rw) wait for child 4 of 4 returned 3 (expected 3)
+(syn-rw) end
+EOF
+pass;
diff --git a/src/tests/filesys/extended/syn-rw.h b/src/tests/filesys/extended/syn-rw.h
new file mode 100644
index 0000000..170aeb4
--- /dev/null
+++ b/src/tests/filesys/extended/syn-rw.h
@@ -0,0 +1,9 @@
+#ifndef TESTS_FILESYS_EXTENDED_SYN_RW_H
+#define TESTS_FILESYS_EXTENDED_SYN_RW_H
+
+#define CHUNK_SIZE 8
+#define CHUNK_CNT 512
+#define BUF_SIZE (CHUNK_SIZE * CHUNK_CNT)
+static const char file_name[] = "logfile";
+
+#endif /* tests/filesys/extended/syn-rw.h */
diff --git a/src/tests/filesys/extended/tar.c b/src/tests/filesys/extended/tar.c
new file mode 100644
index 0000000..9801484
--- /dev/null
+++ b/src/tests/filesys/extended/tar.c
@@ -0,0 +1,208 @@
+/* tar.c
+
+   Creates a tar archive. */
+
+#include <ustar.h>
+#include <syscall.h>
+#include <stdio.h>
+#include <string.h>
+
+static void usage (void);
+static bool make_tar_archive (const char *archive_name,
+                              char *files[], size_t file_cnt);
+
+int
+main (int argc, char *argv[]) 
+{
+  if (argc < 3)
+    usage ();
+
+  return (make_tar_archive (argv[1], argv + 2, argc - 2)
+          ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static void
+usage (void) 
+{
+  printf ("tar, tar archive creator\n"
+          "Usage: tar ARCHIVE FILE...\n"
+          "where ARCHIVE is the tar archive to create\n"
+          "  and FILE... is a list of files or directories to put into it.\n"
+          "(ARCHIVE itself will not be included in the archive, even if it\n"
+          "is in a directory to be archived.)\n");
+  exit (EXIT_FAILURE);
+}
+
+static bool archive_file (char file_name[], size_t file_name_size,
+                          int archive_fd, bool *write_error);
+
+static bool archive_ordinary_file (const char *file_name, int file_fd,
+                                   int archive_fd, bool *write_error);
+static bool archive_directory (char file_name[], size_t file_name_size,
+                               int file_fd, int archive_fd, bool *write_error);
+static bool write_header (const char *file_name, enum ustar_type, int size,
+                          int archive_fd, bool *write_error);
+
+static bool do_write (int fd, const char *buffer, int size, bool *write_error);
+
+static bool
+make_tar_archive (const char *archive_name, char *files[], size_t file_cnt) 
+{
+  static const char zeros[512];
+  int archive_fd;
+  bool success = true;
+  bool write_error = false;
+  size_t i;
+  
+  if (!create (archive_name, 0)) 
+    {
+      printf ("%s: create failed\n", archive_name);
+      return false;
+    }
+  archive_fd = open (archive_name);
+  if (archive_fd < 0)
+    {
+      printf ("%s: open failed\n", archive_name);
+      return false;
+    }
+
+  for (i = 0; i < file_cnt; i++) 
+    {
+      char file_name[128];
+      
+      strlcpy (file_name, files[i], sizeof file_name);
+      if (!archive_file (file_name, sizeof file_name,
+                         archive_fd, &write_error))
+        success = false;
+    }
+
+  if (!do_write (archive_fd, zeros, 512, &write_error)
+      || !do_write (archive_fd, zeros, 512, &write_error)) 
+    success = false;
+
+  close (archive_fd);
+
+  return success;
+}
+
+static bool
+archive_file (char file_name[], size_t file_name_size,
+              int archive_fd, bool *write_error) 
+{
+  int file_fd = open (file_name);
+  if (file_fd >= 0) 
+    {
+      bool success;
+
+      if (inumber (file_fd) != inumber (archive_fd)) 
+        {
+          if (!isdir (file_fd))
+            success = archive_ordinary_file (file_name, file_fd,
+                                             archive_fd, write_error);
+          else
+            success = archive_directory (file_name, file_name_size, file_fd,
+                                         archive_fd, write_error);      
+        }
+      else
+        {
+          /* Nothing to do: don't try to archive the archive file. */
+          success = true;
+        }
+  
+      close (file_fd);
+
+      return success;
+    }
+  else
+    {
+      printf ("%s: open failed\n", file_name);
+      return false;
+    }
+}
+
+static bool
+archive_ordinary_file (const char *file_name, int file_fd,
+                       int archive_fd, bool *write_error)
+{
+  bool read_error = false;
+  bool success = true;
+  int file_size = filesize (file_fd);
+
+  if (!write_header (file_name, USTAR_REGULAR, file_size,
+                     archive_fd, write_error))
+    return false;
+
+  while (file_size > 0) 
+    {
+      static char buf[512];
+      int chunk_size = file_size > 512 ? 512 : file_size;
+      int read_retval = read (file_fd, buf, chunk_size);
+      int bytes_read = read_retval > 0 ? read_retval : 0;
+
+      if (bytes_read != chunk_size && !read_error) 
+        {
+          printf ("%s: read error\n", file_name);
+          read_error = true;
+          success = false;
+        }
+
+      memset (buf + bytes_read, 0, 512 - bytes_read);
+      if (!do_write (archive_fd, buf, 512, write_error))
+        success = false;
+
+      file_size -= chunk_size;
+    }
+
+  return success;
+}
+
+static bool
+archive_directory (char file_name[], size_t file_name_size, int file_fd,
+                   int archive_fd, bool *write_error)
+{
+  size_t dir_len;
+  bool success = true;
+
+  dir_len = strlen (file_name);
+  if (dir_len + 1 + READDIR_MAX_LEN + 1 > file_name_size) 
+    {
+      printf ("%s: file name too long\n", file_name);
+      return false;
+    }
+
+  if (!write_header (file_name, USTAR_DIRECTORY, 0, archive_fd, write_error))
+    return false;
+      
+  file_name[dir_len] = '/';
+  while (readdir (file_fd, &file_name[dir_len + 1])) 
+    if (!archive_file (file_name, file_name_size, archive_fd, write_error))
+      success = false;
+  file_name[dir_len] = '\0';
+
+  return success;
+}
+
+static bool
+write_header (const char *file_name, enum ustar_type type, int size,
+              int archive_fd, bool *write_error) 
+{
+  static char header[512];
+  return (ustar_make_header (file_name, type, size, header)
+          && do_write (archive_fd, header, 512, write_error));
+}
+
+static bool
+do_write (int fd, const char *buffer, int size, bool *write_error) 
+{
+  if (write (fd, buffer, size) == size) 
+    return true;
+  else
+    {
+      if (!*write_error) 
+        {
+          printf ("error writing archive\n");
+          *write_error = true; 
+        }
+      return false; 
+    }
+}
diff --git a/src/tests/filesys/seq-test.c b/src/tests/filesys/seq-test.c
new file mode 100644
index 0000000..8ce222c
--- /dev/null
+++ b/src/tests/filesys/seq-test.c
@@ -0,0 +1,37 @@
+#include "tests/filesys/seq-test.h"
+#include <random.h>
+#include <syscall.h>
+#include "tests/lib.h"
+
+void 
+seq_test (const char *file_name, void *buf, size_t size, size_t initial_size,
+          size_t (*block_size_func) (void),
+          void (*check_func) (int fd, long ofs)) 
+{
+  size_t ofs;
+  int fd;
+  
+  random_bytes (buf, size);
+  CHECK (create (file_name, initial_size), "create \"%s\"", file_name);
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\"", file_name);
+
+  ofs = 0;
+  msg ("writing \"%s\"", file_name);
+  while (ofs < size) 
+    {
+      size_t block_size = block_size_func ();
+      if (block_size > size - ofs)
+        block_size = size - ofs;
+
+      if (write (fd, buf + ofs, block_size) != (int) block_size)
+        fail ("write %zu bytes at offset %zu in \"%s\" failed",
+              block_size, ofs, file_name);
+
+      ofs += block_size;
+      if (check_func != NULL)
+        check_func (fd, ofs);
+    }
+  msg ("close \"%s\"", file_name);
+  close (fd);
+  check_file (file_name, buf, size);
+}
diff --git a/src/tests/filesys/seq-test.h b/src/tests/filesys/seq-test.h
new file mode 100644
index 0000000..0697381
--- /dev/null
+++ b/src/tests/filesys/seq-test.h
@@ -0,0 +1,11 @@
+#ifndef TESTS_FILESYS_SEQ_TEST_H
+#define TESTS_FILESYS_SEQ_TEST_H
+
+#include <stddef.h>
+
+void seq_test (const char *file_name,
+               void *buf, size_t size, size_t initial_size,
+               size_t (*block_size_func) (void),
+               void (*check_func) (int fd, long ofs));
+
+#endif /* tests/filesys/seq-test.h */
diff --git a/src/tests/internal/list.c b/src/tests/internal/list.c
new file mode 100644
index 0000000..836c69e
--- /dev/null
+++ b/src/tests/internal/list.c
@@ -0,0 +1,174 @@
+/* Test program for lib/kernel/list.c.
+
+   Attempts to test the list functionality that is not
+   sufficiently tested elsewhere in Pintos.
+
+   This is not a test we will run on your submitted projects.
+   It is here for completeness.
+*/
+
+#undef NDEBUG
+#include <debug.h>
+#include <list.h>
+#include <random.h>
+#include <stdio.h>
+#include "threads/test.h"
+
+/* Maximum number of elements in a linked list that we will
+   test. */
+#define MAX_SIZE 64
+
+/* A linked list element. */
+struct value 
+  {
+    struct list_elem elem;      /* List element. */
+    int value;                  /* Item value. */
+  };
+
+static void shuffle (struct value[], size_t);
+static bool value_less (const struct list_elem *, const struct list_elem *,
+                        void *);
+static void verify_list_fwd (struct list *, int size);
+static void verify_list_bkwd (struct list *, int size);
+
+/* Test the linked list implementation. */
+void
+test (void) 
+{
+  int size;
+
+  printf ("testing various size lists:");
+  for (size = 0; size < MAX_SIZE; size++) 
+    {
+      int repeat;
+
+      printf (" %d", size);
+      for (repeat = 0; repeat < 10; repeat++) 
+        {
+          static struct value values[MAX_SIZE * 4];
+          struct list list;
+          struct list_elem *e;
+          int i, ofs;
+
+          /* Put values 0...SIZE in random order in VALUES. */
+          for (i = 0; i < size; i++)
+            values[i].value = i;
+          shuffle (values, size);
+  
+          /* Assemble list. */
+          list_init (&list);
+          for (i = 0; i < size; i++)
+            list_push_back (&list, &values[i].elem);
+
+          /* Verify correct minimum and maximum elements. */
+          e = list_min (&list, value_less, NULL);
+          ASSERT (size ? list_entry (e, struct value, elem)->value == 0
+                  : e == list_begin (&list));
+          e = list_max (&list, value_less, NULL);
+          ASSERT (size ? list_entry (e, struct value, elem)->value == size - 1
+                  : e == list_begin (&list));
+
+          /* Sort and verify list. */
+          list_sort (&list, value_less, NULL);
+          verify_list_fwd (&list, size);
+
+          /* Reverse and verify list. */
+          list_reverse (&list);
+          verify_list_bkwd (&list, size);
+
+          /* Shuffle, insert using list_insert_ordered(),
+             and verify ordering. */
+          shuffle (values, size);
+          list_init (&list);
+          for (i = 0; i < size; i++)
+            list_insert_ordered (&list, &values[i].elem,
+                                 value_less, NULL);
+          verify_list_fwd (&list, size);
+
+          /* Duplicate some items, uniquify, and verify. */
+          ofs = size;
+          for (e = list_begin (&list); e != list_end (&list);
+               e = list_next (e))
+            {
+              struct value *v = list_entry (e, struct value, elem);
+              int copies = random_ulong () % 4;
+              while (copies-- > 0) 
+                {
+                  values[ofs].value = v->value;
+                  list_insert (e, &values[ofs++].elem);
+                }
+            }
+          ASSERT ((size_t) ofs < sizeof values / sizeof *values);
+          list_unique (&list, NULL, value_less, NULL);
+          verify_list_fwd (&list, size);
+        }
+    }
+  
+  printf (" done\n");
+  printf ("list: PASS\n");
+}
+
+/* Shuffles the CNT elements in ARRAY into random order. */
+static void
+shuffle (struct value *array, size_t cnt) 
+{
+  size_t i;
+
+  for (i = 0; i < cnt; i++)
+    {
+      size_t j = i + random_ulong () % (cnt - i);
+      struct value t = array[j];
+      array[j] = array[i];
+      array[i] = t;
+    }
+}
+
+/* Returns true if value A is less than value B, false
+   otherwise. */
+static bool
+value_less (const struct list_elem *a_, const struct list_elem *b_,
+            void *aux UNUSED) 
+{
+  const struct value *a = list_entry (a_, struct value, elem);
+  const struct value *b = list_entry (b_, struct value, elem);
+  
+  return a->value < b->value;
+}
+
+/* Verifies that LIST contains the values 0...SIZE when traversed
+   in forward order. */
+static void
+verify_list_fwd (struct list *list, int size) 
+{
+  struct list_elem *e;
+  int i;
+  
+  for (i = 0, e = list_begin (list);
+       i < size && e != list_end (list);
+       i++, e = list_next (e)) 
+    {
+      struct value *v = list_entry (e, struct value, elem);
+      ASSERT (i == v->value);
+    }
+  ASSERT (i == size);
+  ASSERT (e == list_end (list));
+}
+
+/* Verifies that LIST contains the values 0...SIZE when traversed
+   in reverse order. */
+static void
+verify_list_bkwd (struct list *list, int size) 
+{
+  struct list_elem *e;
+  int i;
+
+  for (i = 0, e = list_rbegin (list);
+       i < size && e != list_rend (list);
+       i++, e = list_prev (e)) 
+    {
+      struct value *v = list_entry (e, struct value, elem);
+      ASSERT (i == v->value);
+    }
+  ASSERT (i == size);
+  ASSERT (e == list_rend (list));
+}
diff --git a/src/tests/internal/stdio.c b/src/tests/internal/stdio.c
new file mode 100644
index 0000000..fb60cda
--- /dev/null
+++ b/src/tests/internal/stdio.c
@@ -0,0 +1,208 @@
+/* Test program for printf() in lib/stdio.c.
+
+   Attempts to test printf() functionality that is not
+   sufficiently tested elsewhere in Pintos.
+
+   This is not a test we will run on your submitted projects.
+   It is here for completeness.
+*/
+
+#undef NDEBUG
+#include <limits.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "threads/test.h"
+
+/* Number of failures so far. */
+static int failure_cnt;
+
+static void
+checkf (const char *expect, const char *format, ...) 
+{
+  char output[128];
+  va_list args;
+
+  printf ("\"%s\" -> \"%s\": ", format, expect);
+  
+  va_start (args, format);
+  vsnprintf (output, sizeof output, format, args);
+  va_end (args);
+
+  if (strcmp (expect, output)) 
+    {
+      printf ("\nFAIL: actual output \"%s\"\n", output);
+      failure_cnt++;
+    }
+  else
+    printf ("okay\n");
+}
+
+/* Test printf() implementation. */
+void
+test (void) 
+{
+  printf ("Testing formats:");
+
+  /* Check that commas show up in the right places, for positive
+     numbers. */
+  checkf ("1", "%'d", 1);
+  checkf ("12", "%'d", 12);
+  checkf ("123", "%'d", 123);
+  checkf ("1,234", "%'d", 1234);
+  checkf ("12,345", "%'d", 12345);
+  checkf ("123,456", "%'ld", 123456L);
+  checkf ("1,234,567", "%'ld", 1234567L);
+  checkf ("12,345,678", "%'ld", 12345678L);
+  checkf ("123,456,789", "%'ld", 123456789L);
+  checkf ("1,234,567,890", "%'ld", 1234567890L);
+  checkf ("12,345,678,901", "%'lld", 12345678901LL);
+  checkf ("123,456,789,012", "%'lld", 123456789012LL);
+  checkf ("1,234,567,890,123", "%'lld", 1234567890123LL);
+  checkf ("12,345,678,901,234", "%'lld", 12345678901234LL);
+  checkf ("123,456,789,012,345", "%'lld", 123456789012345LL);
+  checkf ("1,234,567,890,123,456", "%'lld", 1234567890123456LL);
+  checkf ("12,345,678,901,234,567", "%'lld", 12345678901234567LL);
+  checkf ("123,456,789,012,345,678", "%'lld", 123456789012345678LL);
+  checkf ("1,234,567,890,123,456,789", "%'lld", 1234567890123456789LL);
+
+  /* Check that commas show up in the right places, for positive
+     numbers. */
+  checkf ("-1", "%'d", -1);
+  checkf ("-12", "%'d", -12);
+  checkf ("-123", "%'d", -123);
+  checkf ("-1,234", "%'d", -1234);
+  checkf ("-12,345", "%'d", -12345);
+  checkf ("-123,456", "%'ld", -123456L);
+  checkf ("-1,234,567", "%'ld", -1234567L);
+  checkf ("-12,345,678", "%'ld", -12345678L);
+  checkf ("-123,456,789", "%'ld", -123456789L);
+  checkf ("-1,234,567,890", "%'ld", -1234567890L);
+  checkf ("-12,345,678,901", "%'lld", -12345678901LL);
+  checkf ("-123,456,789,012", "%'lld", -123456789012LL);
+  checkf ("-1,234,567,890,123", "%'lld", -1234567890123LL);
+  checkf ("-12,345,678,901,234", "%'lld", -12345678901234LL);
+  checkf ("-123,456,789,012,345", "%'lld", -123456789012345LL);
+  checkf ("-1,234,567,890,123,456", "%'lld", -1234567890123456LL);
+  checkf ("-12,345,678,901,234,567", "%'lld", -12345678901234567LL);
+  checkf ("-123,456,789,012,345,678", "%'lld", -123456789012345678LL);
+  checkf ("-1,234,567,890,123,456,789", "%'lld", -1234567890123456789LL);
+  
+  /* Check signed integer conversions. */
+  checkf ("    0", "%5d", 0);
+  checkf ("0    ", "%-5d", 0);
+  checkf ("   +0", "%+5d", 0);
+  checkf ("+0   ", "%+-5d", 0);
+  checkf ("    0", "% 5d", 0);
+  checkf ("00000", "%05d", 0);
+  checkf ("     ", "%5.0d", 0);
+  checkf ("   00", "%5.2d", 0);
+  checkf ("0", "%d", 0);
+
+  checkf ("    1", "%5d", 1);
+  checkf ("1    ", "%-5d", 1);
+  checkf ("   +1", "%+5d", 1);
+  checkf ("+1   ", "%+-5d", 1);
+  checkf ("    1", "% 5d", 1);
+  checkf ("00001", "%05d", 1);
+  checkf ("    1", "%5.0d", 1);
+  checkf ("   01", "%5.2d", 1);
+  checkf ("1", "%d", 1);
+
+  checkf ("   -1", "%5d", -1);
+  checkf ("-1   ", "%-5d", -1);
+  checkf ("   -1", "%+5d", -1);
+  checkf ("-1   ", "%+-5d", -1);
+  checkf ("   -1", "% 5d", -1);
+  checkf ("-0001", "%05d", -1);
+  checkf ("   -1", "%5.0d", -1);
+  checkf ("  -01", "%5.2d", -1);
+  checkf ("-1", "%d", -1);
+
+  checkf ("12345", "%5d", 12345);
+  checkf ("12345", "%-5d", 12345);
+  checkf ("+12345", "%+5d", 12345);
+  checkf ("+12345", "%+-5d", 12345);
+  checkf (" 12345", "% 5d", 12345);
+  checkf ("12345", "%05d", 12345);
+  checkf ("12345", "%5.0d", 12345);
+  checkf ("12345", "%5.2d", 12345);
+  checkf ("12345", "%d", 12345);
+
+  checkf ("123456", "%5d", 123456);
+  checkf ("123456", "%-5d", 123456);
+  checkf ("+123456", "%+5d", 123456);
+  checkf ("+123456", "%+-5d", 123456);
+  checkf (" 123456", "% 5d", 123456);
+  checkf ("123456", "%05d", 123456);
+  checkf ("123456", "%5.0d", 123456);
+  checkf ("123456", "%5.2d", 123456);
+  checkf ("123456", "%d", 123456);
+
+  /* Check unsigned integer conversions. */
+  checkf ("    0", "%5u", 0);
+  checkf ("    0", "%5o", 0);
+  checkf ("    0", "%5x", 0);
+  checkf ("    0", "%5X", 0);
+  checkf ("    0", "%#5o", 0);
+  checkf ("    0", "%#5x", 0);
+  checkf ("    0", "%#5X", 0);
+  checkf ("  00000000", "%#10.8x", 0);
+  
+  checkf ("    1", "%5u", 1);
+  checkf ("    1", "%5o", 1);
+  checkf ("    1", "%5x", 1);
+  checkf ("    1", "%5X", 1);
+  checkf ("   01", "%#5o", 1);
+  checkf ("  0x1", "%#5x", 1);
+  checkf ("  0X1", "%#5X", 1);
+  checkf ("0x00000001", "%#10.8x", 1);
+
+  checkf ("123456", "%5u", 123456);
+  checkf ("361100", "%5o", 123456);
+  checkf ("1e240", "%5x", 123456);
+  checkf ("1E240", "%5X", 123456);
+  checkf ("0361100", "%#5o", 123456);
+  checkf ("0x1e240", "%#5x", 123456);
+  checkf ("0X1E240", "%#5X", 123456);
+  checkf ("0x0001e240", "%#10.8x", 123456);
+
+  /* Character and string conversions. */
+  checkf ("foobar", "%c%c%c%c%c%c", 'f', 'o', 'o', 'b', 'a', 'r');
+  checkf ("  left-right  ", "%6s%s%-7s", "left", "-", "right");
+  checkf ("trim", "%.4s", "trimoff");
+  checkf ("%%", "%%%%");
+
+  /* From Cristian Cadar's automatic test case generator. */
+  checkf (" abcdefgh", "%9s", "abcdefgh");
+  checkf ("36657730000", "%- o", (unsigned) 036657730000);
+  checkf ("4139757568", "%- u", (unsigned) 4139757568UL);
+  checkf ("f6bfb000", "%- x", (unsigned) 0xf6bfb000);
+  checkf ("36657730000", "%-to", (ptrdiff_t) 036657730000);
+  checkf ("4139757568", "%-tu", (ptrdiff_t) 4139757568UL);
+  checkf ("-155209728", "%-zi", (size_t) -155209728);
+  checkf ("-155209728", "%-zd", (size_t) -155209728);
+  checkf ("036657730000", "%+#o", (unsigned) 036657730000);
+  checkf ("0xf6bfb000", "%+#x", (unsigned) 0xf6bfb000);
+  checkf ("-155209728", "% zi", (size_t) -155209728);
+  checkf ("-155209728", "% zd", (size_t) -155209728);
+  checkf ("4139757568", "% tu", (ptrdiff_t) 4139757568UL);
+  checkf ("036657730000", "% #o", (unsigned) 036657730000);
+  checkf ("0xf6bfb000", "% #x", (unsigned) 0xf6bfb000);
+  checkf ("0xf6bfb000", "%# x", (unsigned) 0xf6bfb000);
+  checkf ("-155209728", "%#zd", (size_t) -155209728);
+  checkf ("-155209728", "%0zi", (size_t) -155209728);
+  checkf ("4,139,757,568", "%'tu", (ptrdiff_t) 4139757568UL);
+  checkf ("-155,209,728", "%-'d", -155209728);
+  checkf ("-155209728", "%.zi", (size_t) -155209728);
+  checkf ("-155209728", "%zi", (size_t) -155209728);
+  checkf ("-155209728", "%zd", (size_t) -155209728);
+  checkf ("-155209728", "%+zi", (size_t) -155209728);
+
+  if (failure_cnt == 0)
+    printf ("\nstdio: PASS\n");
+  else
+    printf ("\nstdio: FAIL: %d tests failed\n", failure_cnt);
+}                                                                  
diff --git a/src/tests/internal/stdlib.c b/src/tests/internal/stdlib.c
new file mode 100644
index 0000000..ad0f0f9
--- /dev/null
+++ b/src/tests/internal/stdlib.c
@@ -0,0 +1,114 @@
+/* Test program for sorting and searching in lib/stdlib.c.
+
+   Attempts to test the sorting and searching functionality that
+   is not sufficiently tested elsewhere in Pintos.
+
+   This is not a test we will run on your submitted projects.
+   It is here for completeness.
+*/
+
+#undef NDEBUG
+#include <debug.h>
+#include <limits.h>
+#include <random.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include "threads/test.h"
+
+/* Maximum number of elements in an array that we will test. */
+#define MAX_CNT 4096
+
+static void shuffle (int[], size_t);
+static int compare_ints (const void *, const void *);
+static void verify_order (const int[], size_t);
+static void verify_bsearch (const int[], size_t);
+
+/* Test sorting and searching implementations. */
+void
+test (void) 
+{
+  int cnt;
+
+  printf ("testing various size arrays:");
+  for (cnt = 0; cnt < MAX_CNT; cnt = cnt * 4 / 3 + 1)
+    {
+      int repeat;
+
+      printf (" %zu", cnt);
+      for (repeat = 0; repeat < 10; repeat++) 
+        {
+          static int values[MAX_CNT];
+          int i;
+
+          /* Put values 0...CNT in random order in VALUES. */
+          for (i = 0; i < cnt; i++)
+            values[i] = i;
+          shuffle (values, cnt);
+  
+          /* Sort VALUES, then verify ordering. */
+          qsort (values, cnt, sizeof *values, compare_ints);
+          verify_order (values, cnt);
+          verify_bsearch (values, cnt);
+        }
+    }
+  
+  printf (" done\n");
+  printf ("stdlib: PASS\n");
+}
+
+/* Shuffles the CNT elements in ARRAY into random order. */
+static void
+shuffle (int *array, size_t cnt) 
+{
+  size_t i;
+
+  for (i = 0; i < cnt; i++)
+    {
+      size_t j = i + random_ulong () % (cnt - i);
+      int t = array[j];
+      array[j] = array[i];
+      array[i] = t;
+    }
+}
+
+/* Returns 1 if *A is greater than *B,
+   0 if *A equals *B,
+   -1 if *A is less than *B. */
+static int
+compare_ints (const void *a_, const void *b_) 
+{
+  const int *a = a_;
+  const int *b = b_;
+
+  return *a < *b ? -1 : *a > *b;
+}
+
+/* Verifies that ARRAY contains the CNT ints 0...CNT-1. */
+static void
+verify_order (const int *array, size_t cnt) 
+{
+  int i;
+
+  for (i = 0; (size_t) i < cnt; i++) 
+    ASSERT (array[i] == i);
+}
+
+/* Checks that bsearch() works properly in ARRAY.  ARRAY must
+   contain the values 0...CNT-1. */
+static void
+verify_bsearch (const int *array, size_t cnt) 
+{
+  int not_in_array[] = {0, -1, INT_MAX, MAX_CNT, MAX_CNT + 1, MAX_CNT * 2};
+  int i;
+
+  /* Check that all the values in the array are found properly. */
+  for (i = 0; (size_t) i < cnt; i++) 
+    ASSERT (bsearch (&i, array, cnt, sizeof *array, compare_ints)
+            == array + i);
+
+  /* Check that some values not in the array are not found. */
+  not_in_array[0] = cnt;
+  for (i = 0; (size_t) i < sizeof not_in_array / sizeof *not_in_array; i++) 
+    ASSERT (bsearch (&not_in_array[i], array, cnt, sizeof *array, compare_ints)
+            == NULL);
+}
diff --git a/src/tests/lib.c b/src/tests/lib.c
new file mode 100644
index 0000000..ee36505
--- /dev/null
+++ b/src/tests/lib.c
@@ -0,0 +1,196 @@
+#include "tests/lib.h"
+#include <random.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+
+const char *test_name;
+bool quiet = false;
+
+static void
+vmsg (const char *format, va_list args, const char *suffix) 
+{
+  /* We go to some trouble to stuff the entire message into a
+     single buffer and output it in a single system call, because
+     that'll (typically) ensure that it gets sent to the console
+     atomically.  Otherwise kernel messages like "foo: exit(0)"
+     can end up being interleaved if we're unlucky. */
+  static char buf[1024];
+
+  snprintf (buf, sizeof buf, "(%s) ", test_name);
+  vsnprintf (buf + strlen (buf), sizeof buf - strlen (buf), format, args);
+  strlcpy (buf + strlen (buf), suffix, sizeof buf - strlen (buf));
+  write (STDOUT_FILENO, buf, strlen (buf));
+}
+
+void
+msg (const char *format, ...) 
+{
+  va_list args;
+
+  if (quiet)
+    return;
+  va_start (args, format);
+  vmsg (format, args, "\n");
+  va_end (args);
+}
+
+void
+fail (const char *format, ...) 
+{
+  va_list args;
+
+  va_start (args, format);
+  vmsg (format, args, ": FAILED\n");
+  va_end (args);
+
+  exit (1);
+}
+
+static void
+swap (void *a_, void *b_, size_t size) 
+{
+  uint8_t *a = a_;
+  uint8_t *b = b_;
+  size_t i;
+
+  for (i = 0; i < size; i++) 
+    {
+      uint8_t t = a[i];
+      a[i] = b[i];
+      b[i] = t;
+    }
+}
+
+void
+shuffle (void *buf_, size_t cnt, size_t size) 
+{
+  char *buf = buf_;
+  size_t i;
+
+  for (i = 0; i < cnt; i++)
+    {
+      size_t j = i + random_ulong () % (cnt - i);
+      swap (buf + i * size, buf + j * size, size);
+    }
+}
+
+void
+exec_children (const char *child_name, pid_t pids[], size_t child_cnt) 
+{
+  size_t i;
+
+  for (i = 0; i < child_cnt; i++) 
+    {
+      char cmd_line[128];
+      snprintf (cmd_line, sizeof cmd_line, "%s %zu", child_name, i);
+      CHECK ((pids[i] = exec (cmd_line)) != PID_ERROR,
+             "exec child %zu of %zu: \"%s\"", i + 1, child_cnt, cmd_line);
+    }
+}
+
+void
+wait_children (pid_t pids[], size_t child_cnt) 
+{
+  size_t i;
+  
+  for (i = 0; i < child_cnt; i++) 
+    {
+      int status = wait (pids[i]);
+      CHECK (status == (int) i,
+             "wait for child %zu of %zu returned %d (expected %zu)",
+             i + 1, child_cnt, status, i);
+    }
+}
+
+void
+check_file_handle (int fd,
+                   const char *file_name, const void *buf_, size_t size) 
+{
+  const char *buf = buf_;
+  size_t ofs = 0;
+  size_t file_size;
+
+  /* Warn about file of wrong size.  Don't fail yet because we
+     may still be able to get more information by reading the
+     file. */
+  file_size = filesize (fd);
+  if (file_size != size)
+    msg ("size of %s (%zu) differs from expected (%zu)",
+          file_name, file_size, size);
+
+  /* Read the file block-by-block, comparing data as we go. */
+  while (ofs < size)
+    {
+      char block[512];
+      size_t block_size, ret_val;
+
+      block_size = size - ofs;
+      if (block_size > sizeof block)
+        block_size = sizeof block;
+
+      ret_val = read (fd, block, block_size);
+      if (ret_val != block_size)
+        fail ("read of %zu bytes at offset %zu in \"%s\" returned %zu",
+              block_size, ofs, file_name, ret_val);
+
+      compare_bytes (block, buf + ofs, block_size, ofs, file_name);
+      ofs += block_size;
+    }
+
+  /* Now fail due to wrong file size. */
+  if (file_size != size)
+    fail ("size of %s (%zu) differs from expected (%zu)",
+          file_name, file_size, size);
+
+  msg ("verified contents of \"%s\"", file_name);
+}
+
+void
+check_file (const char *file_name, const void *buf, size_t size) 
+{
+  int fd;
+
+  CHECK ((fd = open (file_name)) > 1, "open \"%s\" for verification",
+         file_name);
+  check_file_handle (fd, file_name, buf, size);
+  msg ("close \"%s\"", file_name);
+  close (fd);
+}
+
+void
+compare_bytes (const void *read_data_, const void *expected_data_, size_t size,
+               size_t ofs, const char *file_name) 
+{
+  const uint8_t *read_data = read_data_;
+  const uint8_t *expected_data = expected_data_;
+  size_t i, j;
+  size_t show_cnt;
+
+  if (!memcmp (read_data, expected_data, size))
+    return;
+  
+  for (i = 0; i < size; i++)
+    if (read_data[i] != expected_data[i])
+      break;
+  for (j = i + 1; j < size; j++)
+    if (read_data[j] == expected_data[j])
+      break;
+
+  quiet = false;
+  msg ("%zu bytes read starting at offset %zu in \"%s\" differ "
+       "from expected.", j - i, ofs + i, file_name);
+  show_cnt = j - i;
+  if (j - i > 64) 
+    {
+      show_cnt = 64;
+      msg ("Showing first differing %zu bytes.", show_cnt);
+    }
+  msg ("Data actually read:");
+  hex_dump (ofs + i, read_data + i, show_cnt, true);
+  msg ("Expected data:");
+  hex_dump (ofs + i, expected_data + i, show_cnt, true);
+  fail ("%zu bytes read starting at offset %zu in \"%s\" differ "
+        "from expected", j - i, ofs + i, file_name);
+}
diff --git a/src/tests/lib.h b/src/tests/lib.h
new file mode 100644
index 0000000..648327b
--- /dev/null
+++ b/src/tests/lib.h
@@ -0,0 +1,50 @@
+#ifndef TESTS_LIB_H
+#define TESTS_LIB_H
+
+#include <debug.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <syscall.h>
+
+extern const char *test_name;
+extern bool quiet;
+
+void msg (const char *, ...) PRINTF_FORMAT (1, 2);
+void fail (const char *, ...) PRINTF_FORMAT (1, 2) NO_RETURN;
+
+/* Takes an expression to test for SUCCESS and a message, which
+   may include printf-style arguments.  Logs the message, then
+   tests the expression.  If it is zero, indicating failure,
+   emits the message as a failure.
+
+   Somewhat tricky to use:
+
+     - SUCCESS must not have side effects that affect the
+       message, because that will cause the original message and
+       the failure message to differ.
+
+     - The message must not have side effects of its own, because
+       it will be printed twice on failure, or zero times on
+       success if quiet is set. */
+#define CHECK(SUCCESS, ...)                     \
+        do                                      \
+          {                                     \
+            msg (__VA_ARGS__);                  \
+            if (!(SUCCESS))                     \
+              fail (__VA_ARGS__);               \
+          }                                     \
+        while (0)
+
+void shuffle (void *, size_t cnt, size_t size);
+
+void exec_children (const char *child_name, pid_t pids[], size_t child_cnt);
+void wait_children (pid_t pids[], size_t child_cnt);
+
+void check_file_handle (int fd, const char *file_name,
+                        const void *buf_, size_t filesize);
+void check_file (const char *file_name, const void *buf, size_t filesize);
+
+void compare_bytes (const void *read_data, const void *expected_data,
+                    size_t size, size_t ofs, const char *file_name);
+
+#endif /* test/lib.h */
diff --git a/src/tests/lib.pm b/src/tests/lib.pm
new file mode 100644
index 0000000..bc37ae5
--- /dev/null
+++ b/src/tests/lib.pm
@@ -0,0 +1,19 @@
+use strict;
+use warnings;
+
+use tests::random;
+
+sub shuffle {
+    my ($in, $cnt, $sz) = @_;
+    $cnt * $sz == length $in or die;
+    my (@a) = 0...$cnt - 1;
+    for my $i (0...$cnt - 1) {
+	my ($j) = $i + random_ulong () % ($cnt - $i);
+	@a[$i, $j] = @a[$j, $i];
+    }
+    my ($out) = "";
+    $out .= substr ($in, $_ * $sz, $sz) foreach @a;
+    return $out;
+}
+
+1;
diff --git a/src/tests/main.c b/src/tests/main.c
new file mode 100644
index 0000000..ad1b0f1
--- /dev/null
+++ b/src/tests/main.c
@@ -0,0 +1,15 @@
+#include <random.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+int
+main (int argc UNUSED, char *argv[]) 
+{
+  test_name = argv[0];
+
+  msg ("begin");
+  random_init (0);
+  test_main ();
+  msg ("end");
+  return 0;
+}
diff --git a/src/tests/main.h b/src/tests/main.h
new file mode 100644
index 0000000..f0e8818
--- /dev/null
+++ b/src/tests/main.h
@@ -0,0 +1,6 @@
+#ifndef TESTS_MAIN_H
+#define TESTS_MAIN_H
+
+void test_main (void);
+
+#endif /* tests/main.h */
diff --git a/src/tests/make-grade b/src/tests/make-grade
new file mode 100644
index 0000000..a3faa0e
--- /dev/null
+++ b/src/tests/make-grade
@@ -0,0 +1,152 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+
+@ARGV == 3 || die;
+my ($src_dir, $results_file, $grading_file) = @ARGV;
+
+# Read pass/file verdicts from $results_file.
+open (RESULTS, '<', $results_file) || die "$results_file: open: $!\n";
+my (%verdicts, %verdict_counts);
+while (<RESULTS>) {
+    my ($verdict, $test) = /^(pass|FAIL) (.*)$/ or die;
+    $verdicts{$test} = $verdict eq 'pass';
+}
+close RESULTS;
+
+my (@failures);
+my (@overall, @rubrics, @summary);
+my ($pct_actual, $pct_possible) = (0, 0);
+
+# Read grading file.
+my (@items);
+open (GRADING, '<', $grading_file) || die "$grading_file: open: $!\n";
+while (<GRADING>) {
+    s/#.*//;
+    next if /^\s*$/;
+    my ($max_pct, $rubric_suffix) = /^\s*(\d+(?:\.\d+)?)%\t(.*)/ or die;
+    my ($dir) = $rubric_suffix =~ /^(.*)\//;
+    my ($rubric_file) = "$src_dir/$rubric_suffix";
+    open (RUBRIC, '<', $rubric_file) or die "$rubric_file: open: $!\n";
+
+    # Rubric file must begin with title line.
+    my $title = <RUBRIC>;
+    chomp $title;
+    $title =~ s/:$// or die;
+    $title .= " ($rubric_suffix):";
+    push (@rubrics, $title);
+
+    my ($score, $possible) = (0, 0);
+    my ($cnt, $passed) = (0, 0);
+    my ($was_score) = 0;
+    while (<RUBRIC>) {
+	chomp;
+	push (@rubrics, "\t$_"), next if /^-/;
+	push (@rubrics, ""), next if /^\s*$/;
+	my ($poss, $name) = /^(\d+)\t(.*)$/ or die;
+	my ($test) = "$dir/$name";
+	my ($points) = 0;
+	if (!defined $verdicts{$test}) {
+	    push (@overall, "warning: $test not tested, assuming failure");
+	} elsif ($verdicts{$test}) {
+	    $points = $poss;
+	    $passed++;
+	}
+	push (@failures, $test) if !$points;
+	$verdict_counts{$test}++;
+	push (@rubrics, sprintf ("\t%4s%2d/%2d %s",
+				 $points ? '' : '**', $points, $poss, $test));
+	$score += $points;
+	$possible += $poss;
+	$cnt++;
+    }
+    close (RUBRIC);
+
+    push (@rubrics, "");
+    push (@rubrics, "\t- Section summary.");
+    push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
+			     '', $passed, $cnt, 'tests passed'));
+    push (@rubrics, sprintf ("\t%4s%3d/%3d %s",
+			     '', $score, $possible, 'points subtotal'));
+    push (@rubrics, '');
+
+    my ($pct) = ($score / $possible) * $max_pct;
+    push (@summary, sprintf ("%-45s %3d/%3d %5.1f%%/%5.1f%%",
+			     $rubric_suffix,
+			     $score, $possible,
+			     $pct, $max_pct));
+    $pct_actual += $pct;
+    $pct_possible += $max_pct;
+}
+close GRADING;
+
+my ($sum_line)
+  = "--------------------------------------------- --- --- ------ ------";
+unshift (@summary,
+	 "SUMMARY BY TEST SET",
+	 '',
+	 sprintf ("%-45s %3s %3s %6s %6s",
+		  "Test Set", "Pts", "Max", "% Ttl", "% Max"),
+	 $sum_line);
+push (@summary,
+      $sum_line,
+      sprintf ("%-45s %3s %3s %5.1f%%/%5.1f%%",
+	       'Total', '', '', $pct_actual, $pct_possible));
+
+unshift (@rubrics,
+	 "SUMMARY OF INDIVIDUAL TESTS",
+	 '');
+
+foreach my $name (keys (%verdicts)) {
+    my ($count) = $verdict_counts{$name};
+    if (!defined ($count) || $count != 1) {
+	if (!defined ($count) || !$count) {
+	    push (@overall, "warning: test $name doesn't count for grading");
+	} else {
+	    push (@overall,
+		  "warning: test $name counted $count times in grading");
+	}
+    }
+}
+push (@overall, sprintf ("TOTAL TESTING SCORE: %.1f%%", $pct_actual));
+if (sprintf ("%.1f", $pct_actual) eq sprintf ("%.1f", $pct_possible)) {
+    push (@overall, "ALL TESTED PASSED -- PERFECT SCORE");
+}
+
+my (@divider) = ('', '- ' x 38, '');
+
+print map ("$_\n", @overall, @divider, @summary, @divider, @rubrics);
+
+for my $test (@failures) {
+    print map ("$_\n", @divider);
+    print "DETAILS OF $test FAILURE:\n\n";
+
+    if (open (RESULT, '<', "$test.result")) {
+	my $first_line = <RESULT>;
+	my ($cnt) = 0;
+	while (<RESULT>) {
+	    print;
+	    $cnt++;
+	}
+	close (RESULT);
+    }
+
+    if (open (OUTPUT, '<', "$test.output")) {
+	print "\nOUTPUT FROM $test:\n\n";
+    
+	my ($panics, $boots) = (0, 0);
+	while (<OUTPUT>) {
+	    if (/PANIC/ && ++$panics > 2) {
+		print "[...details of additional panic(s) omitted...]\n";
+		last;
+	    }
+	    print;
+	    if (/Pintos booting/ && ++$boots > 1) {
+		print "[...details of reboot(s) omitted...]\n";
+		last;
+	    }
+	}
+	close (OUTPUT);
+    }
+}
diff --git a/src/tests/random.pm b/src/tests/random.pm
new file mode 100644
index 0000000..be008ff
--- /dev/null
+++ b/src/tests/random.pm
@@ -0,0 +1,27 @@
+use strict;
+use warnings;
+
+use tests::arc4;
+
+my (@arc4);
+
+sub random_init {
+    if (@arc4 == 0) {
+	my ($seed) = @_;
+	$seed = 0 if !defined $seed;
+	@arc4 = arc4_init (pack ("V", $seed));
+    }
+}
+
+sub random_bytes {
+    random_init ();
+    my ($n) = @_;
+    return arc4_crypt (\@arc4, "\0" x $n);
+}
+
+sub random_ulong {
+    random_init ();
+    return unpack ("V", random_bytes (4));
+}
+
+1;
diff --git a/src/tests/tests.pm b/src/tests/tests.pm
new file mode 100644
index 0000000..4599cb9
--- /dev/null
+++ b/src/tests/tests.pm
@@ -0,0 +1,625 @@
+use strict;
+use warnings;
+use tests::Algorithm::Diff;
+use File::Temp 'tempfile';
+use Fcntl qw(SEEK_SET SEEK_CUR);
+
+sub fail;
+sub pass;
+
+die if @ARGV != 2;
+our ($test, $src_dir) = @ARGV;
+
+my ($msg_file) = tempfile ();
+select ($msg_file);
+
+our (@prereq_tests) = ();
+if ($test =~ /^(.*)-persistence$/) {
+    push (@prereq_tests, $1);
+}
+for my $prereq_test (@prereq_tests) {
+    my (@result) = read_text_file ("$prereq_test.result");
+    fail "Prerequisite test $prereq_test failed.\n" if $result[0] ne 'PASS';
+}
+
+
+# Generic testing.
+
+sub check_expected {
+    my ($expected) = pop @_;
+    my (@options) = @_;
+    my (@output) = read_text_file ("$test.output");
+    common_checks ("run", @output);
+    compare_output ("run", @options, \@output, $expected);
+}
+
+sub common_checks {
+    my ($run, @output) = @_;
+
+    fail "\u$run produced no output at all\n" if @output == 0;
+
+    check_for_panic ($run, @output);
+    check_for_keyword ($run, "FAIL", @output);
+    check_for_triple_fault ($run, @output);
+    check_for_keyword ($run, "TIMEOUT", @output);
+
+    fail "\u$run didn't start up properly: no \"Pintos booting\" message\n"
+      if !grep (/Pintos booting with.*kB RAM\.\.\./, @output);
+    fail "\u$run didn't start up properly: no \"Boot complete\" message\n"
+      if !grep (/Boot complete/, @output);
+    fail "\u$run didn't shut down properly: no \"Timer: # ticks\" message\n"
+      if !grep (/Timer: \d+ ticks/, @output);
+    fail "\u$run didn't shut down properly: no \"Powering off\" message\n"
+      if !grep (/Powering off/, @output);
+}
+
+sub check_for_panic {
+    my ($run, @output) = @_;
+
+    my ($panic) = grep (/PANIC/, @output);
+    return unless defined $panic;
+
+    print "Kernel panic in $run: ", substr ($panic, index ($panic, "PANIC")),
+      "\n";
+
+    my (@stack_line) = grep (/Call stack:/, @output);
+    if (@stack_line != 0) {
+	my ($addrs) = $stack_line[0] =~ /Call stack:((?: 0x[0-9a-f]+)+)/;
+
+	# Find a user program to translate user virtual addresses.
+	my ($userprog) = "";
+	$userprog = "$test"
+	  if grep (hex ($_) < 0xc0000000, split (' ', $addrs)) > 0 && -e $test;
+
+	# Get and print the backtrace.
+	my ($trace) = scalar (`backtrace kernel.o $userprog $addrs`);
+	print "Call stack:$addrs\n";
+	print "Translation of call stack:\n";
+	print $trace;
+
+	# Print disclaimer.
+	if ($userprog ne '' && index ($trace, $userprog) >= 0) {
+	    print <<EOF;
+Translations of user virtual addresses above are based on a guess at
+the binary to use.  If this guess is incorrect, then those
+translations will be misleading.
+EOF
+	}
+    }
+
+    if ($panic =~ /sec_no \< d-\>capacity/) {
+	print <<EOF;
+\nThis assertion commonly fails when accessing a file via an inode that
+has been closed and freed.  Freeing an inode clears all its sector
+indexes to 0xcccccccc, which is not a valid sector number for disks
+smaller than about 1.6 TB.
+EOF
+    }
+
+    fail;
+}
+
+sub check_for_keyword {
+    my ($run, $keyword, @output) = @_;
+    
+    my ($kw_line) = grep (/$keyword/, @output);
+    return unless defined $kw_line;
+
+    # Most output lines are prefixed by (test-name).  Eliminate this
+    # from our message for brevity.
+    $kw_line =~ s/^\([^\)]+\)\s+//;
+    print "$run: $kw_line\n";
+
+    fail;
+}
+
+sub check_for_triple_fault {
+    my ($run, @output) = @_;
+
+    my ($reboots) = grep (/Pintos booting/, @output) - 1;
+    return unless $reboots > 0;
+
+    print <<EOF;
+\u$run spontaneously rebooted $reboots times.
+This is most often caused by unhandled page faults.
+Read the Triple Faults section in the Debugging chapter
+of the Pintos manual for more information.
+EOF
+
+    fail;
+}
+
+# Get @output without header or trailer.
+sub get_core_output {
+    my ($run, @output) = @_;
+    my ($p);
+
+    my ($process);
+    my ($start);
+    for my $i (0...$#_) {
+	$start = $i + 1, last
+	  if ($process) = $output[$i] =~ /^Executing '(\S+).*':$/;
+    }
+
+    my ($end);
+    for my $i ($start...$#output) {
+	$end = $i - 1, last if $output[$i] =~ /^Execution of '.*' complete.$/;
+    }
+
+    fail "\u$run didn't start a thread or process\n" if !defined $start;
+    fail "\u$run started '$process' but it never finished\n" if !defined $end;
+
+    return @output[$start...$end];
+}
+
+sub compare_output {
+    my ($run) = shift @_;
+    my ($expected) = pop @_;
+    my ($output) = pop @_;
+    my (%options) = @_;
+
+    my (@output) = get_core_output ($run, @$output);
+    fail "\u$run didn't produce any output" if !@output;
+
+    my $ignore_exit_codes = exists $options{IGNORE_EXIT_CODES};
+    if ($ignore_exit_codes) {
+	delete $options{IGNORE_EXIT_CODES};
+	@output = grep (!/^[a-zA-Z0-9-_]+: exit\(\-?\d+\)$/, @output);
+    }
+    my $ignore_user_faults = exists $options{IGNORE_USER_FAULTS};
+    if ($ignore_user_faults) {
+	delete $options{IGNORE_USER_FAULTS};
+	@output = grep (!/^Page fault at.*in user context\.$/
+			&& !/: dying due to interrupt 0x0e \(.*\).$/
+			&& !/^Interrupt 0x0e \(.*\) at eip=/
+			&& !/^ cr2=.* error=.*/
+			&& !/^ eax=.* ebx=.* ecx=.* edx=.*/
+			&& !/^ esi=.* edi=.* esp=.* ebp=.*/
+			&& !/^ cs=.* ds=.* es=.* ss=.*/, @output);
+    }
+    die "unknown option " . (keys (%options))[0] . "\n" if %options;
+
+    my ($msg);
+
+    # Compare actual output against each allowed output.
+    if (ref ($expected) eq 'ARRAY') {
+	my ($i) = 0;
+	$expected = {map ((++$i => $_), @$expected)};
+    }
+    foreach my $key (keys %$expected) {
+	my (@expected) = split ("\n", $expected->{$key});
+
+	$msg .= "Acceptable output:\n";
+	$msg .= join ('', map ("  $_\n", @expected));
+
+	# Check whether actual and expected match.
+	# If it's a perfect match, we're done.
+	if ($#output == $#expected) {
+	    my ($eq) = 1;
+	    for (my ($i) = 0; $i <= $#expected; $i++) {
+		$eq = 0 if $output[$i] ne $expected[$i];
+	    }
+	    return $key if $eq;
+	}
+
+	# They differ.  Output a diff.
+	my (@diff) = "";
+	my ($d) = Algorithm::Diff->new (\@expected, \@output);
+	while ($d->Next ()) {
+	    my ($ef, $el, $af, $al) = $d->Get (qw (min1 max1 min2 max2));
+	    if ($d->Same ()) {
+		push (@diff, map ("  $_\n", $d->Items (1)));
+	    } else {
+		push (@diff, map ("- $_\n", $d->Items (1))) if $d->Items (1);
+		push (@diff, map ("+ $_\n", $d->Items (2))) if $d->Items (2);
+	    }
+	}
+
+	$msg .= "Differences in `diff -u' format:\n";
+	$msg .= join ('', @diff);
+    }
+
+    # Failed to match.  Report failure.
+    $msg .= "\n(Process exit codes are excluded for matching purposes.)\n"
+      if $ignore_exit_codes;
+    $msg .= "\n(User fault messages are excluded for matching purposes.)\n"
+      if $ignore_user_faults;
+    fail "Test output failed to match any acceptable form.\n\n$msg";
+}
+
+# File system extraction.
+
+# check_archive (\%CONTENTS)
+#
+# Checks that the extracted file system's contents match \%CONTENTS.
+# Each key in the hash is a file name.  Each value may be:
+#
+#	- $FILE: Name of a host file containing the expected contents.
+#
+#	- [$FILE, $OFFSET, $LENGTH]: An excerpt of host file $FILE
+#	  comprising the $LENGTH bytes starting at $OFFSET.
+#
+#	- [$CONTENTS]: The literal expected file contents, as a string.
+#
+#       - {SUBDIR}: A subdirectory, in the same form described here,
+#         recursively.
+sub check_archive {
+    my ($expected_hier) = @_;
+
+    my (@output) = read_text_file ("$test.output");
+    common_checks ("file system extraction run", @output);
+
+    @output = get_core_output ("file system extraction run", @output);
+    @output = grep (!/^[a-zA-Z0-9-_]+: exit\(\d+\)$/, @output);
+    fail join ("\n", "Error extracting file system:", @output) if @output;
+
+    my ($test_base_name) = $test;
+    $test_base_name =~ s%.*/%%;
+    $test_base_name =~ s%-persistence$%%;
+    $expected_hier->{$test_base_name} = $prereq_tests[0];
+    $expected_hier->{'tar'} = 'tests/filesys/extended/tar';
+
+    my (%expected) = normalize_fs (flatten_hierarchy ($expected_hier, ""));
+    my (%actual) = read_tar ("$prereq_tests[0].tar");
+
+    my ($errors) = 0;
+    foreach my $name (sort keys %expected) {
+	if (exists $actual{$name}) {
+	    if (is_dir ($actual{$name}) && !is_dir ($expected{$name})) {
+		print "$name is a directory but should be an ordinary file.\n";
+		$errors++;
+	    } elsif (!is_dir ($actual{$name}) && is_dir ($expected{$name})) {
+		print "$name is an ordinary file but should be a directory.\n";
+		$errors++;
+	    }
+	} else {
+	    print "$name is missing from the file system.\n";
+	    $errors++;
+	}
+    }
+    foreach my $name (sort keys %actual) {
+	if (!exists $expected{$name}) {
+	    if ($name =~ /^[[:print:]]+$/) {
+		print "$name exists in the file system but it should not.\n";
+	    } else {
+		my ($esc_name) = $name;
+		$esc_name =~ s/[^[:print:]]/./g;
+		print <<EOF;
+$esc_name exists in the file system but should not.  (The name
+of this file contains unusual characters that were printed as `.'.)
+EOF
+	    }
+	    $errors++;
+ 	}
+    }
+    if ($errors) {
+	print "\nActual contents of file system:\n";
+	print_fs (%actual);
+	print "\nExpected contents of file system:\n";
+	print_fs (%expected);
+    } else {
+	foreach my $name (sort keys %expected) {
+	    if (!is_dir ($expected{$name})) {
+		my ($exp_file, $exp_length) = open_file ($expected{$name});
+		my ($act_file, $act_length) = open_file ($actual{$name});
+		$errors += !compare_files ($exp_file, $exp_length,
+					   $act_file, $act_length, $name,
+					   !$errors);
+		close ($exp_file);
+		close ($act_file);
+	    }
+	}
+    }
+    fail "Extracted file system contents are not correct.\n" if $errors;
+}
+
+# open_file ([$FILE, $OFFSET, $LENGTH])
+# open_file ([$CONTENTS])
+#
+# Opens a file for the contents passed in, which must be in one of
+# the two above forms that correspond to check_archive() arguments.
+#
+# Returns ($HANDLE, $LENGTH), where $HANDLE is the file's handle and
+# $LENGTH is the number of bytes in the file's content.
+sub open_file {
+    my ($value) = @_;
+    die if ref ($value) ne 'ARRAY';
+
+    my ($file) = tempfile ();
+    my ($length);
+    if (@$value == 1) {
+	$length = length ($value->[0]);
+	$file = tempfile ();
+	syswrite ($file, $value->[0]) == $length
+	  or die "writing temporary file: $!\n";
+	sysseek ($file, 0, SEEK_SET);
+    } elsif (@$value == 3) {
+	$length = $value->[2];
+	open ($file, '<', $value->[0]) or die "$value->[0]: open: $!\n";
+	die "$value->[0]: file is smaller than expected\n"
+	  if -s $file < $value->[1] + $length;
+	sysseek ($file, $value->[1], SEEK_SET);
+    } else {
+	die;
+    }
+    return ($file, $length);
+}
+
+# compare_files ($A, $A_SIZE, $B, $B_SIZE, $NAME, $VERBOSE)
+#
+# Compares $A_SIZE bytes in $A to $B_SIZE bytes in $B.
+# ($A and $B are handles.)
+# If their contents differ, prints a brief message describing
+# the differences, using $NAME to identify the file.
+# The message contains more detail if $VERBOSE is nonzero.
+# Returns 1 if the contents are identical, 0 otherwise.
+sub compare_files {
+    my ($a, $a_size, $b, $b_size, $name, $verbose) = @_;
+    my ($ofs) = 0;
+    select(STDOUT);
+    for (;;) {
+	my ($a_amt) = $a_size >= 1024 ? 1024 : $a_size;
+	my ($b_amt) = $b_size >= 1024 ? 1024 : $b_size;
+	my ($a_data, $b_data);
+	if (!defined (sysread ($a, $a_data, $a_amt))
+	    || !defined (sysread ($b, $b_data, $b_amt))) {
+	    die "reading $name: $!\n";
+	}
+
+	my ($a_len) = length $a_data;
+	my ($b_len) = length $b_data;
+	last if $a_len == 0 && $b_len == 0;
+
+	if ($a_data ne $b_data) {
+	    my ($min_len) = $a_len < $b_len ? $a_len : $b_len;
+	    my ($diff_ofs);
+	    for ($diff_ofs = 0; $diff_ofs < $min_len; $diff_ofs++) {
+		last if (substr ($a_data, $diff_ofs, 1)
+			 ne substr ($b_data, $diff_ofs, 1));
+	    }
+
+	    printf "\nFile $name differs from expected "
+	      . "starting at offset 0x%x.\n", $ofs + $diff_ofs;
+	    if ($verbose ) {
+		print "Expected contents:\n";
+		hex_dump (substr ($a_data, $diff_ofs, 64), $ofs + $diff_ofs);
+		print "Actual contents:\n";
+		hex_dump (substr ($b_data, $diff_ofs, 64), $ofs + $diff_ofs);
+	    }
+	    return 0;
+	}
+
+	$ofs += $a_len;
+	$a_size -= $a_len;
+	$b_size -= $b_len;
+    }
+    return 1;
+}
+
+# hex_dump ($DATA, $OFS)
+#
+# Prints $DATA in hex and text formats.
+# The first byte of $DATA corresponds to logical offset $OFS
+# in whatever file the data comes from.
+sub hex_dump {
+    my ($data, $ofs) = @_;
+
+    if ($data eq '') {
+	printf "  (File ends at offset %08x.)\n", $ofs;
+	return;
+    }
+
+    my ($per_line) = 16;
+    while ((my $size = length ($data)) > 0) {
+	my ($start) = $ofs % $per_line;
+	my ($end) = $per_line;
+	$end = $start + $size if $end - $start > $size;
+	my ($n) = $end - $start;
+
+	printf "0x%08x  ", int ($ofs / $per_line) * $per_line;
+
+	# Hex version.
+	print "   " x $start;
+	for my $i ($start...$end - 1) {
+	    printf "%02x", ord (substr ($data, $i - $start, 1));
+	    print $i == $per_line / 2 - 1 ? '-' : ' ';
+	}
+	print "   " x ($per_line - $end);
+
+	# Character version.
+	my ($esc_data) = substr ($data, 0, $n);
+	$esc_data =~ s/[^[:print:]]/./g;
+	print "|", " " x $start, $esc_data, " " x ($per_line - $end), "|";
+
+	print "\n";
+
+	$data = substr ($data, $n);
+	$ofs += $n;
+    }
+}
+
+# print_fs (%FS)
+#
+# Prints a list of files in %FS, which must be a file system
+# as flattened by flatten_hierarchy() and normalized by
+# normalize_fs().
+sub print_fs {
+    my (%fs) = @_;
+    foreach my $name (sort keys %fs) {
+	my ($esc_name) = $name;
+	$esc_name =~ s/[^[:print:]]/./g;
+	print "$esc_name: ";
+	if (!is_dir ($fs{$name})) {
+	    print +file_size ($fs{$name}), "-byte file";
+	} else {
+	    print "directory";
+	}
+	print "\n";
+    }
+    print "(empty)\n" if !@_;
+}
+
+# normalize_fs (%FS)
+#
+# Takes a file system as flattened by flatten_hierarchy().
+# Returns a similar file system in which values of the form $FILE
+# are replaced by those of the form [$FILE, $OFFSET, $LENGTH].
+sub normalize_fs {
+    my (%fs) = @_;
+    foreach my $name (keys %fs) {
+	my ($value) = $fs{$name};
+	next if is_dir ($value) || ref ($value) ne '';
+	die "can't open $value\n" if !stat $value;
+	$fs{$name} = [$value, 0, -s _];
+    }
+    return %fs;
+}
+
+# is_dir ($VALUE)
+#
+# Takes a value like one in the hash returned by flatten_hierarchy()
+# and returns 1 if it represents a directory, 0 otherwise.
+sub is_dir {
+    my ($value) = @_;
+    return ref ($value) eq '' && $value eq 'directory';
+}
+
+# file_size ($VALUE)
+#
+# Takes a value like one in the hash returned by flatten_hierarchy()
+# and returns the size of the file it represents.
+sub file_size {
+    my ($value) = @_;
+    die if is_dir ($value);
+    die if ref ($value) ne 'ARRAY';
+    return @$value > 1 ? $value->[2] : length ($value->[0]);
+}
+
+# flatten_hierarchy ($HIER_FS, $PREFIX)
+#
+# Takes a file system in the format expected by check_archive() and
+# returns a "flattened" version in which file names include all parent
+# directory names and the value of directories is just "directory".
+sub flatten_hierarchy {
+    my (%hier_fs) = %{$_[0]};
+    my ($prefix) = $_[1];
+    my (%flat_fs);
+    for my $name (keys %hier_fs) {
+	my ($value) = $hier_fs{$name};
+	if (ref $value eq 'HASH') {
+	    %flat_fs = (%flat_fs, flatten_hierarchy ($value, "$prefix$name/"));
+	    $flat_fs{"$prefix$name"} = 'directory';
+	} else {
+	    $flat_fs{"$prefix$name"} = $value;
+	}
+    }
+    return %flat_fs;
+}
+
+# read_tar ($ARCHIVE)
+#
+# Reads the ustar-format tar file in $ARCHIVE
+# and returns a flattened file system for it.
+sub read_tar {
+    my ($archive) = @_;
+    my (%content);
+    open (ARCHIVE, '<', $archive) or fail "$archive: open: $!\n";
+    for (;;) {
+	my ($header);
+	if ((my $retval = sysread (ARCHIVE, $header, 512)) != 512) {
+	    fail "$archive: unexpected end of file\n" if $retval >= 0;
+	    fail "$archive: read: $!\n";
+	}
+
+	last if $header eq "\0" x 512;
+
+	# Verify magic numbers.
+	if (substr ($header, 257, 6) ne "ustar\0"
+	    || substr ($header, 263, 2) ne '00') {
+	    fail "$archive: corrupt ustar header\n";
+	}
+
+	# Verify checksum.
+	my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8, ' ' x 8)));
+	my ($correct_chksum) = unpack ("%32a*", $header);
+	fail "$archive: bad header checksum\n" if $chksum != $correct_chksum;
+
+	# Get file name.
+	my ($name) = unpack ("Z100", $header);
+	my ($prefix) = unpack ("Z*", substr ($header, 345));
+	$name = "$prefix/$name" if $prefix ne '';
+	fail "$archive: contains file with empty name" if $name eq '';
+
+	# Get type.
+	my ($typeflag) = substr ($header, 156, 1);
+	$typeflag = '0' if $typeflag eq "\0";
+	fail "unknown file type '$typeflag'\n" if $typeflag !~ /[05]/;
+
+	# Get size.
+	my ($size) = oct (unpack ("Z*", substr ($header, 124, 12)));
+	fail "bad size $size\n" if $size < 0;
+	$size = 0 if $typeflag eq '5';
+
+	# Store content.
+	$name =~ s%^(/|\./|\.\./)*%%;	# Strip leading "/", "./", "../".
+	$name = '' if $name eq '.' || $name eq '..';
+	if (exists $content{$name}) {
+	    fail "$archive: contains multiple entries for $name\n";
+	}
+	if ($typeflag eq '5') {
+	    $content{$name} = 'directory' if $name ne '';
+	} else {
+	    fail "$archive: contains file with empty name\n" if $name eq '';
+	    my ($position) = sysseek (ARCHIVE, 0, SEEK_CUR);
+	    $content{$name} = [$archive, $position, $size];
+	    sysseek (ARCHIVE, int (($size + 511) / 512) * 512, SEEK_CUR);
+	}
+    }
+    close (ARCHIVE);
+    return %content;
+}
+
+# Utilities.
+
+sub fail {
+    finish ("FAIL", @_);
+}
+
+sub pass {
+    finish ("PASS", @_);
+}
+
+sub finish {
+    my ($verdict, @messages) = @_;
+
+    seek ($msg_file, 0, 0);
+    push (@messages, <$msg_file>);
+    close ($msg_file);
+    chomp (@messages);
+
+    my ($result_fn) = "$test.result";
+    open (RESULT, '>', $result_fn) or die "$result_fn: create: $!\n";
+    print RESULT "$verdict\n";
+    print RESULT "$_\n" foreach @messages;
+    close (RESULT);
+
+    if ($verdict eq 'PASS') {
+	print STDOUT "pass $test\n";
+    } else {
+	print STDOUT "FAIL $test\n";
+    }
+    print STDOUT "$_\n" foreach @messages;
+
+    exit 0;
+}
+
+sub read_text_file {
+    my ($file_name) = @_;
+    open (FILE, '<', $file_name) or die "$file_name: open: $!\n";
+    my (@content) = <FILE>;
+    chomp (@content);
+    close (FILE);
+    return @content;
+}
+
+1;
diff --git a/src/tests/threads/Grading b/src/tests/threads/Grading
new file mode 100644
index 0000000..c9be35f
--- /dev/null
+++ b/src/tests/threads/Grading
@@ -0,0 +1,6 @@
+# Percentage of the testing point total designated for each set of
+# tests.
+
+20.0%	tests/threads/Rubric.alarm
+40.0%	tests/threads/Rubric.priority
+40.0%	tests/threads/Rubric.mlfqs
diff --git a/src/tests/threads/Make.tests b/src/tests/threads/Make.tests
new file mode 100644
index 0000000..4569035
--- /dev/null
+++ b/src/tests/threads/Make.tests
@@ -0,0 +1,53 @@
+# -*- makefile -*-
+
+# Test names.
+tests/threads_TESTS = $(addprefix tests/threads/,alarm-single		\
+alarm-multiple alarm-simultaneous alarm-priority alarm-zero		\
+alarm-negative priority-change priority-donate-one			\
+priority-donate-multiple priority-donate-multiple2			\
+priority-donate-nest priority-donate-sema priority-donate-lower		\
+priority-fifo priority-preempt priority-sema priority-condvar		\
+priority-donate-chain                                                   \
+mlfqs-load-1 mlfqs-load-60 mlfqs-load-avg mlfqs-recent-1 mlfqs-fair-2	\
+mlfqs-fair-20 mlfqs-nice-2 mlfqs-nice-10 mlfqs-block)
+
+# Sources for tests.
+tests/threads_SRC  = tests/threads/tests.c
+tests/threads_SRC += tests/threads/alarm-wait.c
+tests/threads_SRC += tests/threads/alarm-simultaneous.c
+tests/threads_SRC += tests/threads/alarm-priority.c
+tests/threads_SRC += tests/threads/alarm-zero.c
+tests/threads_SRC += tests/threads/alarm-negative.c
+tests/threads_SRC += tests/threads/priority-change.c
+tests/threads_SRC += tests/threads/priority-donate-one.c
+tests/threads_SRC += tests/threads/priority-donate-multiple.c
+tests/threads_SRC += tests/threads/priority-donate-multiple2.c
+tests/threads_SRC += tests/threads/priority-donate-nest.c
+tests/threads_SRC += tests/threads/priority-donate-sema.c
+tests/threads_SRC += tests/threads/priority-donate-lower.c
+tests/threads_SRC += tests/threads/priority-fifo.c
+tests/threads_SRC += tests/threads/priority-preempt.c
+tests/threads_SRC += tests/threads/priority-sema.c
+tests/threads_SRC += tests/threads/priority-condvar.c
+tests/threads_SRC += tests/threads/priority-donate-chain.c
+tests/threads_SRC += tests/threads/mlfqs-load-1.c
+tests/threads_SRC += tests/threads/mlfqs-load-60.c
+tests/threads_SRC += tests/threads/mlfqs-load-avg.c
+tests/threads_SRC += tests/threads/mlfqs-recent-1.c
+tests/threads_SRC += tests/threads/mlfqs-fair.c
+tests/threads_SRC += tests/threads/mlfqs-block.c
+
+MLFQS_OUTPUTS = 				\
+tests/threads/mlfqs-load-1.output		\
+tests/threads/mlfqs-load-60.output		\
+tests/threads/mlfqs-load-avg.output		\
+tests/threads/mlfqs-recent-1.output		\
+tests/threads/mlfqs-fair-2.output		\
+tests/threads/mlfqs-fair-20.output		\
+tests/threads/mlfqs-nice-2.output		\
+tests/threads/mlfqs-nice-10.output		\
+tests/threads/mlfqs-block.output
+
+$(MLFQS_OUTPUTS): KERNELFLAGS += -mlfqs
+$(MLFQS_OUTPUTS): TIMEOUT = 480
+
diff --git a/src/tests/threads/Rubric.alarm b/src/tests/threads/Rubric.alarm
new file mode 100644
index 0000000..61abe85
--- /dev/null
+++ b/src/tests/threads/Rubric.alarm
@@ -0,0 +1,8 @@
+Functionality and robustness of alarm clock:
+4	alarm-single
+4	alarm-multiple
+4	alarm-simultaneous
+4	alarm-priority
+
+1	alarm-zero
+1	alarm-negative
diff --git a/src/tests/threads/Rubric.mlfqs b/src/tests/threads/Rubric.mlfqs
new file mode 100644
index 0000000..f260091
--- /dev/null
+++ b/src/tests/threads/Rubric.mlfqs
@@ -0,0 +1,14 @@
+Functionality of advanced scheduler:
+5	mlfqs-load-1
+5	mlfqs-load-60
+3	mlfqs-load-avg
+
+5	mlfqs-recent-1
+
+5	mlfqs-fair-2
+3	mlfqs-fair-20
+
+4	mlfqs-nice-2
+2	mlfqs-nice-10
+
+5	mlfqs-block
diff --git a/src/tests/threads/Rubric.priority b/src/tests/threads/Rubric.priority
new file mode 100644
index 0000000..652bc99
--- /dev/null
+++ b/src/tests/threads/Rubric.priority
@@ -0,0 +1,15 @@
+Functionality of priority scheduler:
+3	priority-change
+3	priority-preempt
+
+3	priority-fifo
+3	priority-sema
+3	priority-condvar
+
+3	priority-donate-one
+3	priority-donate-multiple
+3	priority-donate-multiple2
+3	priority-donate-nest
+5	priority-donate-chain
+3	priority-donate-sema
+3	priority-donate-lower
diff --git a/src/tests/threads/alarm-multiple.ck b/src/tests/threads/alarm-multiple.ck
new file mode 100644
index 0000000..fd83bcd
--- /dev/null
+++ b/src/tests/threads/alarm-multiple.ck
@@ -0,0 +1,4 @@
+# -*- perl -*-
+use tests::tests;
+use tests::threads::alarm;
+check_alarm (7);
diff --git a/src/tests/threads/alarm-negative.c b/src/tests/threads/alarm-negative.c
new file mode 100644
index 0000000..aec52cf
--- /dev/null
+++ b/src/tests/threads/alarm-negative.c
@@ -0,0 +1,15 @@
+/* Tests timer_sleep(-100).  Only requirement is that it not crash. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+void
+test_alarm_negative (void) 
+{
+  timer_sleep (-100);
+  pass ();
+}
diff --git a/src/tests/threads/alarm-negative.ck b/src/tests/threads/alarm-negative.ck
new file mode 100644
index 0000000..0d2bab0
--- /dev/null
+++ b/src/tests/threads/alarm-negative.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(alarm-negative) begin
+(alarm-negative) PASS
+(alarm-negative) end
+EOF
+pass;
diff --git a/src/tests/threads/alarm-priority.c b/src/tests/threads/alarm-priority.c
new file mode 100644
index 0000000..2288ff6
--- /dev/null
+++ b/src/tests/threads/alarm-priority.c
@@ -0,0 +1,58 @@
+/* Checks that when the alarm clock wakes up threads, the
+   higher-priority threads run first. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+static thread_func alarm_priority_thread;
+static int64_t wake_time;
+static struct semaphore wait_sema;
+
+void
+test_alarm_priority (void) 
+{
+  int i;
+  
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  wake_time = timer_ticks () + 5 * TIMER_FREQ;
+  sema_init (&wait_sema, 0);
+  
+  for (i = 0; i < 10; i++) 
+    {
+      int priority = PRI_DEFAULT - (i + 5) % 10 - 1;
+      char name[16];
+      snprintf (name, sizeof name, "priority %d", priority);
+      thread_create (name, priority, alarm_priority_thread, NULL);
+    }
+
+  thread_set_priority (PRI_MIN);
+
+  for (i = 0; i < 10; i++)
+    sema_down (&wait_sema);
+}
+
+static void
+alarm_priority_thread (void *aux UNUSED) 
+{
+  /* Busy-wait until the current time changes. */
+  int64_t start_time = timer_ticks ();
+  while (timer_elapsed (start_time) == 0)
+    continue;
+
+  /* Now we know we're at the very beginning of a timer tick, so
+     we can call timer_sleep() without worrying about races
+     between checking the time and a timer interrupt. */
+  timer_sleep (wake_time - timer_ticks ());
+
+  /* Print a message on wake-up. */
+  msg ("Thread %s woke up.", thread_name ());
+
+  sema_up (&wait_sema);
+}
diff --git a/src/tests/threads/alarm-priority.ck b/src/tests/threads/alarm-priority.ck
new file mode 100644
index 0000000..b57c78b
--- /dev/null
+++ b/src/tests/threads/alarm-priority.ck
@@ -0,0 +1,19 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(alarm-priority) begin
+(alarm-priority) Thread priority 30 woke up.
+(alarm-priority) Thread priority 29 woke up.
+(alarm-priority) Thread priority 28 woke up.
+(alarm-priority) Thread priority 27 woke up.
+(alarm-priority) Thread priority 26 woke up.
+(alarm-priority) Thread priority 25 woke up.
+(alarm-priority) Thread priority 24 woke up.
+(alarm-priority) Thread priority 23 woke up.
+(alarm-priority) Thread priority 22 woke up.
+(alarm-priority) Thread priority 21 woke up.
+(alarm-priority) end
+EOF
+pass;
diff --git a/src/tests/threads/alarm-simultaneous.c b/src/tests/threads/alarm-simultaneous.c
new file mode 100644
index 0000000..844eea4
--- /dev/null
+++ b/src/tests/threads/alarm-simultaneous.c
@@ -0,0 +1,94 @@
+/* Creates N threads, each of which sleeps a different, fixed
+   duration, M times.  Records the wake-up order and verifies
+   that it is valid. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+static void test_sleep (int thread_cnt, int iterations);
+
+void
+test_alarm_simultaneous (void) 
+{
+  test_sleep (3, 5);
+}
+
+/* Information about the test. */
+struct sleep_test 
+  {
+    int64_t start;              /* Current time at start of test. */
+    int iterations;             /* Number of iterations per thread. */
+    int *output_pos;            /* Current position in output buffer. */
+  };
+
+static void sleeper (void *);
+
+/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
+static void
+test_sleep (int thread_cnt, int iterations) 
+{
+  struct sleep_test test;
+  int *output;
+  int i;
+
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
+  msg ("Each thread sleeps 10 ticks each time.");
+  msg ("Within an iteration, all threads should wake up on the same tick.");
+
+  /* Allocate memory. */
+  output = malloc (sizeof *output * iterations * thread_cnt * 2);
+  if (output == NULL)
+    PANIC ("couldn't allocate memory for test");
+
+  /* Initialize test. */
+  test.start = timer_ticks () + 100;
+  test.iterations = iterations;
+  test.output_pos = output;
+
+  /* Start threads. */
+  ASSERT (output != NULL);
+  for (i = 0; i < thread_cnt; i++)
+    {
+      char name[16];
+      snprintf (name, sizeof name, "thread %d", i);
+      thread_create (name, PRI_DEFAULT, sleeper, &test);
+    }
+  
+  /* Wait long enough for all the threads to finish. */
+  timer_sleep (100 + iterations * 10 + 100);
+
+  /* Print completion order. */
+  msg ("iteration 0, thread 0: woke up after %d ticks", output[0]);
+  for (i = 1; i < test.output_pos - output; i++) 
+    msg ("iteration %d, thread %d: woke up %d ticks later",
+         i / thread_cnt, i % thread_cnt, output[i] - output[i - 1]);
+  
+  free (output);
+}
+
+/* Sleeper thread. */
+static void
+sleeper (void *test_) 
+{
+  struct sleep_test *test = test_;
+  int i;
+
+  /* Make sure we're at the beginning of a timer tick. */
+  timer_sleep (1);
+
+  for (i = 1; i <= test->iterations; i++) 
+    {
+      int64_t sleep_until = test->start + i * 10;
+      timer_sleep (sleep_until - timer_ticks ());
+      *test->output_pos++ = timer_ticks () - test->start;
+      thread_yield ();
+    }
+}
diff --git a/src/tests/threads/alarm-simultaneous.ck b/src/tests/threads/alarm-simultaneous.ck
new file mode 100644
index 0000000..406b8b0
--- /dev/null
+++ b/src/tests/threads/alarm-simultaneous.ck
@@ -0,0 +1,27 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(alarm-simultaneous) begin
+(alarm-simultaneous) Creating 3 threads to sleep 5 times each.
+(alarm-simultaneous) Each thread sleeps 10 ticks each time.
+(alarm-simultaneous) Within an iteration, all threads should wake up on the same tick.
+(alarm-simultaneous) iteration 0, thread 0: woke up after 10 ticks
+(alarm-simultaneous) iteration 0, thread 1: woke up 0 ticks later
+(alarm-simultaneous) iteration 0, thread 2: woke up 0 ticks later
+(alarm-simultaneous) iteration 1, thread 0: woke up 10 ticks later
+(alarm-simultaneous) iteration 1, thread 1: woke up 0 ticks later
+(alarm-simultaneous) iteration 1, thread 2: woke up 0 ticks later
+(alarm-simultaneous) iteration 2, thread 0: woke up 10 ticks later
+(alarm-simultaneous) iteration 2, thread 1: woke up 0 ticks later
+(alarm-simultaneous) iteration 2, thread 2: woke up 0 ticks later
+(alarm-simultaneous) iteration 3, thread 0: woke up 10 ticks later
+(alarm-simultaneous) iteration 3, thread 1: woke up 0 ticks later
+(alarm-simultaneous) iteration 3, thread 2: woke up 0 ticks later
+(alarm-simultaneous) iteration 4, thread 0: woke up 10 ticks later
+(alarm-simultaneous) iteration 4, thread 1: woke up 0 ticks later
+(alarm-simultaneous) iteration 4, thread 2: woke up 0 ticks later
+(alarm-simultaneous) end
+EOF
+pass;
diff --git a/src/tests/threads/alarm-single.ck b/src/tests/threads/alarm-single.ck
new file mode 100644
index 0000000..31215df
--- /dev/null
+++ b/src/tests/threads/alarm-single.ck
@@ -0,0 +1,4 @@
+# -*- perl -*-
+use tests::tests;
+use tests::threads::alarm;
+check_alarm (1);
diff --git a/src/tests/threads/alarm-wait.c b/src/tests/threads/alarm-wait.c
new file mode 100644
index 0000000..37d3afc
--- /dev/null
+++ b/src/tests/threads/alarm-wait.c
@@ -0,0 +1,152 @@
+/* Creates N threads, each of which sleeps a different, fixed
+   duration, M times.  Records the wake-up order and verifies
+   that it is valid. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+static void test_sleep (int thread_cnt, int iterations);
+
+void
+test_alarm_single (void) 
+{
+  test_sleep (5, 1);
+}
+
+void
+test_alarm_multiple (void) 
+{
+  test_sleep (5, 7);
+}
+
+/* Information about the test. */
+struct sleep_test 
+  {
+    int64_t start;              /* Current time at start of test. */
+    int iterations;             /* Number of iterations per thread. */
+
+    /* Output. */
+    struct lock output_lock;    /* Lock protecting output buffer. */
+    int *output_pos;            /* Current position in output buffer. */
+  };
+
+/* Information about an individual thread in the test. */
+struct sleep_thread 
+  {
+    struct sleep_test *test;     /* Info shared between all threads. */
+    int id;                     /* Sleeper ID. */
+    int duration;               /* Number of ticks to sleep. */
+    int iterations;             /* Iterations counted so far. */
+  };
+
+static void sleeper (void *);
+
+/* Runs THREAD_CNT threads thread sleep ITERATIONS times each. */
+static void
+test_sleep (int thread_cnt, int iterations) 
+{
+  struct sleep_test test;
+  struct sleep_thread *threads;
+  int *output, *op;
+  int product;
+  int i;
+
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  msg ("Creating %d threads to sleep %d times each.", thread_cnt, iterations);
+  msg ("Thread 0 sleeps 10 ticks each time,");
+  msg ("thread 1 sleeps 20 ticks each time, and so on.");
+  msg ("If successful, product of iteration count and");
+  msg ("sleep duration will appear in nondescending order.");
+
+  /* Allocate memory. */
+  threads = malloc (sizeof *threads * thread_cnt);
+  output = malloc (sizeof *output * iterations * thread_cnt * 2);
+  if (threads == NULL || output == NULL)
+    PANIC ("couldn't allocate memory for test");
+
+  /* Initialize test. */
+  test.start = timer_ticks () + 100;
+  test.iterations = iterations;
+  lock_init (&test.output_lock);
+  test.output_pos = output;
+
+  /* Start threads. */
+  ASSERT (output != NULL);
+  for (i = 0; i < thread_cnt; i++)
+    {
+      struct sleep_thread *t = threads + i;
+      char name[16];
+      
+      t->test = &test;
+      t->id = i;
+      t->duration = (i + 1) * 10;
+      t->iterations = 0;
+
+      snprintf (name, sizeof name, "thread %d", i);
+      thread_create (name, PRI_DEFAULT, sleeper, t);
+    }
+  
+  /* Wait long enough for all the threads to finish. */
+  timer_sleep (100 + thread_cnt * iterations * 10 + 100);
+
+  /* Acquire the output lock in case some rogue thread is still
+     running. */
+  lock_acquire (&test.output_lock);
+
+  /* Print completion order. */
+  product = 0;
+  for (op = output; op < test.output_pos; op++) 
+    {
+      struct sleep_thread *t;
+      int new_prod;
+
+      ASSERT (*op >= 0 && *op < thread_cnt);
+      t = threads + *op;
+
+      new_prod = ++t->iterations * t->duration;
+        
+      msg ("thread %d: duration=%d, iteration=%d, product=%d",
+           t->id, t->duration, t->iterations, new_prod);
+      
+      if (new_prod >= product)
+        product = new_prod;
+      else
+        fail ("thread %d woke up out of order (%d > %d)!",
+              t->id, product, new_prod);
+    }
+
+  /* Verify that we had the proper number of wakeups. */
+  for (i = 0; i < thread_cnt; i++)
+    if (threads[i].iterations != iterations)
+      fail ("thread %d woke up %d times instead of %d",
+            i, threads[i].iterations, iterations);
+  
+  lock_release (&test.output_lock);
+  free (output);
+  free (threads);
+}
+
+/* Sleeper thread. */
+static void
+sleeper (void *t_) 
+{
+  struct sleep_thread *t = t_;
+  struct sleep_test *test = t->test;
+  int i;
+
+  for (i = 1; i <= test->iterations; i++) 
+    {
+      int64_t sleep_until = test->start + i * t->duration;
+      timer_sleep (sleep_until - timer_ticks ());
+      lock_acquire (&test->output_lock);
+      *test->output_pos++ = t->id;
+      lock_release (&test->output_lock);
+    }
+}
diff --git a/src/tests/threads/alarm-zero.c b/src/tests/threads/alarm-zero.c
new file mode 100644
index 0000000..c8a3ee2
--- /dev/null
+++ b/src/tests/threads/alarm-zero.c
@@ -0,0 +1,15 @@
+/* Tests timer_sleep(0), which should return immediately. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+void
+test_alarm_zero (void) 
+{
+  timer_sleep (0);
+  pass ();
+}
diff --git a/src/tests/threads/alarm-zero.ck b/src/tests/threads/alarm-zero.ck
new file mode 100644
index 0000000..a6b1a3c
--- /dev/null
+++ b/src/tests/threads/alarm-zero.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(alarm-zero) begin
+(alarm-zero) PASS
+(alarm-zero) end
+EOF
+pass;
diff --git a/src/tests/threads/alarm.pm b/src/tests/threads/alarm.pm
new file mode 100644
index 0000000..84b3b7f
--- /dev/null
+++ b/src/tests/threads/alarm.pm
@@ -0,0 +1,32 @@
+sub check_alarm {
+    my ($iterations) = @_;
+    our ($test);
+
+    @output = read_text_file ("$test.output");
+    common_checks ("run", @output);
+
+    my (@products);
+    for (my ($i) = 0; $i < $iterations; $i++) {
+	for (my ($t) = 0; $t < 5; $t++) {
+	    push (@products, ($i + 1) * ($t + 1) * 10);
+	}
+    }
+    @products = sort {$a <=> $b} @products;
+
+    local ($_);
+    foreach (@output) {
+	fail $_ if /out of order/i;
+
+	my ($p) = /product=(\d+)$/;
+	next if !defined $p;
+
+	my ($q) = shift (@products);
+	fail "Too many wakeups.\n" if !defined $q;
+	fail "Out of order wakeups ($p vs. $q).\n" if $p != $q; # FIXME
+    }
+    fail scalar (@products) . " fewer wakeups than expected.\n"
+      if @products != 0;
+    pass;
+}
+
+1;
diff --git a/src/tests/threads/mlfqs-block.c b/src/tests/threads/mlfqs-block.c
new file mode 100644
index 0000000..6d4992d
--- /dev/null
+++ b/src/tests/threads/mlfqs-block.c
@@ -0,0 +1,64 @@
+/* Checks that recent_cpu and priorities are updated for blocked
+   threads.
+
+   The main thread sleeps for 25 seconds, spins for 5 seconds,
+   then releases a lock.  The "block" thread spins for 20 seconds
+   then attempts to acquire the lock, which will block for 10
+   seconds (until the main thread releases it).  If recent_cpu
+   decays properly while the "block" thread sleeps, then the
+   block thread should be immediately scheduled when the main
+   thread releases the lock. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+static void block_thread (void *lock_);
+
+void
+test_mlfqs_block (void) 
+{
+  int64_t start_time;
+  struct lock lock;
+  
+  ASSERT (thread_mlfqs);
+
+  msg ("Main thread acquiring lock.");
+  lock_init (&lock);
+  lock_acquire (&lock);
+  
+  msg ("Main thread creating block thread, sleeping 25 seconds...");
+  thread_create ("block", PRI_DEFAULT, block_thread, &lock);
+  timer_sleep (25 * TIMER_FREQ);
+
+  msg ("Main thread spinning for 5 seconds...");
+  start_time = timer_ticks ();
+  while (timer_elapsed (start_time) < 5 * TIMER_FREQ)
+    continue;
+
+  msg ("Main thread releasing lock.");
+  lock_release (&lock);
+
+  msg ("Block thread should have already acquired lock.");
+}
+
+static void
+block_thread (void *lock_) 
+{
+  struct lock *lock = lock_;
+  int64_t start_time;
+
+  msg ("Block thread spinning for 20 seconds...");
+  start_time = timer_ticks ();
+  while (timer_elapsed (start_time) < 20 * TIMER_FREQ)
+    continue;
+
+  msg ("Block thread acquiring lock...");
+  lock_acquire (lock);
+
+  msg ("...got it.");
+}
diff --git a/src/tests/threads/mlfqs-block.ck b/src/tests/threads/mlfqs-block.ck
new file mode 100644
index 0000000..8833a3a
--- /dev/null
+++ b/src/tests/threads/mlfqs-block.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(mlfqs-block) begin
+(mlfqs-block) Main thread acquiring lock.
+(mlfqs-block) Main thread creating block thread, sleeping 25 seconds...
+(mlfqs-block) Block thread spinning for 20 seconds...
+(mlfqs-block) Block thread acquiring lock...
+(mlfqs-block) Main thread spinning for 5 seconds...
+(mlfqs-block) Main thread releasing lock.
+(mlfqs-block) ...got it.
+(mlfqs-block) Block thread should have already acquired lock.
+(mlfqs-block) end
+EOF
+pass;
diff --git a/src/tests/threads/mlfqs-fair-2.ck b/src/tests/threads/mlfqs-fair-2.ck
new file mode 100644
index 0000000..5b19ff1
--- /dev/null
+++ b/src/tests/threads/mlfqs-fair-2.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::threads::mlfqs;
+
+check_mlfqs_fair ([0, 0], 50);
diff --git a/src/tests/threads/mlfqs-fair-20.ck b/src/tests/threads/mlfqs-fair-20.ck
new file mode 100644
index 0000000..bb4d051
--- /dev/null
+++ b/src/tests/threads/mlfqs-fair-20.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::threads::mlfqs;
+
+check_mlfqs_fair ([(0) x 20], 20);
diff --git a/src/tests/threads/mlfqs-fair.c b/src/tests/threads/mlfqs-fair.c
new file mode 100644
index 0000000..3b1bea5
--- /dev/null
+++ b/src/tests/threads/mlfqs-fair.c
@@ -0,0 +1,124 @@
+/* Measures the correctness of the "nice" implementation.
+
+   The "fair" tests run either 2 or 20 threads all niced to 0.
+   The threads should all receive approximately the same number
+   of ticks.  Each test runs for 30 seconds, so the ticks should
+   also sum to approximately 30 * 100 == 3000 ticks.
+
+   The mlfqs-nice-2 test runs 2 threads, one with nice 0, the
+   other with nice 5, which should receive 1,904 and 1,096 ticks,
+   respectively, over 30 seconds.
+
+   The mlfqs-nice-10 test runs 10 threads with nice 0 through 9.
+   They should receive 672, 588, 492, 408, 316, 232, 152, 92, 40,
+   and 8 ticks, respectively, over 30 seconds.
+
+   (The above are computed via simulation in mlfqs.pm.) */
+
+#include <stdio.h>
+#include <inttypes.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/palloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+static void test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step);
+
+void
+test_mlfqs_fair_2 (void) 
+{
+  test_mlfqs_fair (2, 0, 0);
+}
+
+void
+test_mlfqs_fair_20 (void) 
+{
+  test_mlfqs_fair (20, 0, 0);
+}
+
+void
+test_mlfqs_nice_2 (void) 
+{
+  test_mlfqs_fair (2, 0, 5);
+}
+
+void
+test_mlfqs_nice_10 (void) 
+{
+  test_mlfqs_fair (10, 0, 1);
+}
+
+#define MAX_THREAD_CNT 20
+
+struct thread_info 
+  {
+    int64_t start_time;
+    int tick_count;
+    int nice;
+  };
+
+static void load_thread (void *aux);
+
+static void
+test_mlfqs_fair (int thread_cnt, int nice_min, int nice_step)
+{
+  struct thread_info info[MAX_THREAD_CNT];
+  int64_t start_time;
+  int nice;
+  int i;
+
+  ASSERT (thread_mlfqs);
+  ASSERT (thread_cnt <= MAX_THREAD_CNT);
+  ASSERT (nice_min >= -10);
+  ASSERT (nice_step >= 0);
+  ASSERT (nice_min + nice_step * (thread_cnt - 1) <= 20);
+
+  thread_set_nice (-20);
+
+  start_time = timer_ticks ();
+  msg ("Starting %d threads...", thread_cnt);
+  nice = nice_min;
+  for (i = 0; i < thread_cnt; i++) 
+    {
+      struct thread_info *ti = &info[i];
+      char name[16];
+
+      ti->start_time = start_time;
+      ti->tick_count = 0;
+      ti->nice = nice;
+
+      snprintf(name, sizeof name, "load %d", i);
+      thread_create (name, PRI_DEFAULT, load_thread, ti);
+
+      nice += nice_step;
+    }
+  msg ("Starting threads took %"PRId64" ticks.", timer_elapsed (start_time));
+
+  msg ("Sleeping 40 seconds to let threads run, please wait...");
+  timer_sleep (40 * TIMER_FREQ);
+  
+  for (i = 0; i < thread_cnt; i++)
+    msg ("Thread %d received %d ticks.", i, info[i].tick_count);
+}
+
+static void
+load_thread (void *ti_) 
+{
+  struct thread_info *ti = ti_;
+  int64_t sleep_time = 5 * TIMER_FREQ;
+  int64_t spin_time = sleep_time + 30 * TIMER_FREQ;
+  int64_t last_time = 0;
+
+  thread_set_nice (ti->nice);
+  timer_sleep (sleep_time - timer_elapsed (ti->start_time));
+  while (timer_elapsed (ti->start_time) < spin_time) 
+    {
+      int64_t cur_time = timer_ticks ();
+      if (cur_time != last_time)
+        ti->tick_count++;
+      last_time = cur_time;
+    }
+}
diff --git a/src/tests/threads/mlfqs-load-1.c b/src/tests/threads/mlfqs-load-1.c
new file mode 100644
index 0000000..a39eea2
--- /dev/null
+++ b/src/tests/threads/mlfqs-load-1.c
@@ -0,0 +1,60 @@
+/* Verifies that a single busy thread raises the load average to
+   0.5 in 38 to 45 seconds.  The expected time is 42 seconds, as
+   you can verify:
+   perl -e '$i++,$a=(59*$a+1)/60while$a<=.5;print "$i\n"'
+
+   Then, verifies that 10 seconds of inactivity drop the load
+   average back below 0.5 again. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+void
+test_mlfqs_load_1 (void) 
+{
+  int64_t start_time;
+  int elapsed;
+  int load_avg;
+  
+  ASSERT (thread_mlfqs);
+
+  msg ("spinning for up to 45 seconds, please wait...");
+
+  start_time = timer_ticks ();
+  for (;;) 
+    {
+      load_avg = thread_get_load_avg ();
+      ASSERT (load_avg >= 0);
+      elapsed = timer_elapsed (start_time) / TIMER_FREQ;
+      if (load_avg > 100)
+        fail ("load average is %d.%02d "
+              "but should be between 0 and 1 (after %d seconds)",
+              load_avg / 100, load_avg % 100, elapsed);
+      else if (load_avg > 50)
+        break;
+      else if (elapsed > 45)
+        fail ("load average stayed below 0.5 for more than 45 seconds");
+    }
+
+  if (elapsed < 38)
+    fail ("load average took only %d seconds to rise above 0.5", elapsed);
+  msg ("load average rose to 0.5 after %d seconds", elapsed);
+
+  msg ("sleeping for another 10 seconds, please wait...");
+  timer_sleep (TIMER_FREQ * 10);
+
+  load_avg = thread_get_load_avg ();
+  if (load_avg < 0)
+    fail ("load average fell below 0");
+  if (load_avg > 50)
+    fail ("load average stayed above 0.5 for more than 10 seconds");
+  msg ("load average fell back below 0.5 (to %d.%02d)",
+       load_avg / 100, load_avg % 100);
+
+  pass ();
+}
diff --git a/src/tests/threads/mlfqs-load-1.ck b/src/tests/threads/mlfqs-load-1.ck
new file mode 100644
index 0000000..faf0ffa
--- /dev/null
+++ b/src/tests/threads/mlfqs-load-1.ck
@@ -0,0 +1,15 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+
+our ($test);
+my (@output) = read_text_file ("$test.output");
+
+common_checks ("run", @output);
+
+@output = get_core_output ("run", @output);
+fail "missing PASS in output"
+  unless grep ($_ eq '(mlfqs-load-1) PASS', @output);
+
+pass;
diff --git a/src/tests/threads/mlfqs-load-60.c b/src/tests/threads/mlfqs-load-60.c
new file mode 100644
index 0000000..b6a3eb6
--- /dev/null
+++ b/src/tests/threads/mlfqs-load-60.c
@@ -0,0 +1,155 @@
+/* Starts 60 threads that each sleep for 10 seconds, then spin in
+   a tight loop for 60 seconds, and sleep for another 60 seconds.
+   Every 2 seconds after the initial sleep, the main thread
+   prints the load average.
+
+   The expected output is this (some margin of error is allowed):
+
+   After 0 seconds, load average=1.00.
+   After 2 seconds, load average=2.95.
+   After 4 seconds, load average=4.84.
+   After 6 seconds, load average=6.66.
+   After 8 seconds, load average=8.42.
+   After 10 seconds, load average=10.13.
+   After 12 seconds, load average=11.78.
+   After 14 seconds, load average=13.37.
+   After 16 seconds, load average=14.91.
+   After 18 seconds, load average=16.40.
+   After 20 seconds, load average=17.84.
+   After 22 seconds, load average=19.24.
+   After 24 seconds, load average=20.58.
+   After 26 seconds, load average=21.89.
+   After 28 seconds, load average=23.15.
+   After 30 seconds, load average=24.37.
+   After 32 seconds, load average=25.54.
+   After 34 seconds, load average=26.68.
+   After 36 seconds, load average=27.78.
+   After 38 seconds, load average=28.85.
+   After 40 seconds, load average=29.88.
+   After 42 seconds, load average=30.87.
+   After 44 seconds, load average=31.84.
+   After 46 seconds, load average=32.77.
+   After 48 seconds, load average=33.67.
+   After 50 seconds, load average=34.54.
+   After 52 seconds, load average=35.38.
+   After 54 seconds, load average=36.19.
+   After 56 seconds, load average=36.98.
+   After 58 seconds, load average=37.74.
+   After 60 seconds, load average=37.48.
+   After 62 seconds, load average=36.24.
+   After 64 seconds, load average=35.04.
+   After 66 seconds, load average=33.88.
+   After 68 seconds, load average=32.76.
+   After 70 seconds, load average=31.68.
+   After 72 seconds, load average=30.63.
+   After 74 seconds, load average=29.62.
+   After 76 seconds, load average=28.64.
+   After 78 seconds, load average=27.69.
+   After 80 seconds, load average=26.78.
+   After 82 seconds, load average=25.89.
+   After 84 seconds, load average=25.04.
+   After 86 seconds, load average=24.21.
+   After 88 seconds, load average=23.41.
+   After 90 seconds, load average=22.64.
+   After 92 seconds, load average=21.89.
+   After 94 seconds, load average=21.16.
+   After 96 seconds, load average=20.46.
+   After 98 seconds, load average=19.79.
+   After 100 seconds, load average=19.13.
+   After 102 seconds, load average=18.50.
+   After 104 seconds, load average=17.89.
+   After 106 seconds, load average=17.30.
+   After 108 seconds, load average=16.73.
+   After 110 seconds, load average=16.17.
+   After 112 seconds, load average=15.64.
+   After 114 seconds, load average=15.12.
+   After 116 seconds, load average=14.62.
+   After 118 seconds, load average=14.14.
+   After 120 seconds, load average=13.67.
+   After 122 seconds, load average=13.22.
+   After 124 seconds, load average=12.78.
+   After 126 seconds, load average=12.36.
+   After 128 seconds, load average=11.95.
+   After 130 seconds, load average=11.56.
+   After 132 seconds, load average=11.17.
+   After 134 seconds, load average=10.80.
+   After 136 seconds, load average=10.45.
+   After 138 seconds, load average=10.10.
+   After 140 seconds, load average=9.77.
+   After 142 seconds, load average=9.45.
+   After 144 seconds, load average=9.13.
+   After 146 seconds, load average=8.83.
+   After 148 seconds, load average=8.54.
+   After 150 seconds, load average=8.26.
+   After 152 seconds, load average=7.98.
+   After 154 seconds, load average=7.72.
+   After 156 seconds, load average=7.47.
+   After 158 seconds, load average=7.22.
+   After 160 seconds, load average=6.98.
+   After 162 seconds, load average=6.75.
+   After 164 seconds, load average=6.53.
+   After 166 seconds, load average=6.31.
+   After 168 seconds, load average=6.10.
+   After 170 seconds, load average=5.90.
+   After 172 seconds, load average=5.70.
+   After 174 seconds, load average=5.52.
+   After 176 seconds, load average=5.33.
+   After 178 seconds, load average=5.16.
+*/
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+static int64_t start_time;
+
+static void load_thread (void *aux);
+
+#define THREAD_CNT 60
+
+void
+test_mlfqs_load_60 (void) 
+{
+  int i;
+  
+  ASSERT (thread_mlfqs);
+
+  start_time = timer_ticks ();
+  msg ("Starting %d niced load threads...", THREAD_CNT);
+  for (i = 0; i < THREAD_CNT; i++) 
+    {
+      char name[16];
+      snprintf(name, sizeof name, "load %d", i);
+      thread_create (name, PRI_DEFAULT, load_thread, NULL);
+    }
+  msg ("Starting threads took %d seconds.",
+       timer_elapsed (start_time) / TIMER_FREQ);
+  
+  for (i = 0; i < 90; i++) 
+    {
+      int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10);
+      int load_avg;
+      timer_sleep (sleep_until - timer_ticks ());
+      load_avg = thread_get_load_avg ();
+      msg ("After %d seconds, load average=%d.%02d.",
+           i * 2, load_avg / 100, load_avg % 100);
+    }
+}
+
+static void
+load_thread (void *aux UNUSED) 
+{
+  int64_t sleep_time = 10 * TIMER_FREQ;
+  int64_t spin_time = sleep_time + 60 * TIMER_FREQ;
+  int64_t exit_time = spin_time + 60 * TIMER_FREQ;
+
+  thread_set_nice (20);
+  timer_sleep (sleep_time - timer_elapsed (start_time));
+  while (timer_elapsed (start_time) < spin_time)
+    continue;
+  timer_sleep (exit_time - timer_elapsed (start_time));
+}
diff --git a/src/tests/threads/mlfqs-load-60.ck b/src/tests/threads/mlfqs-load-60.ck
new file mode 100644
index 0000000..cb69220
--- /dev/null
+++ b/src/tests/threads/mlfqs-load-60.ck
@@ -0,0 +1,36 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::threads::mlfqs;
+
+our ($test);
+
+my (@output) = read_text_file ("$test.output");
+common_checks ("run", @output);
+@output = get_core_output ("run", @output);
+
+# Get actual values.
+local ($_);
+my (@actual);
+foreach (@output) {
+    my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./
+      or next;
+    $actual[$t] = $load_avg;
+}
+
+# Calculate expected values.
+my ($load_avg) = 0;
+my ($recent) = 0;
+my (@expected);
+for (my ($t) = 0; $t < 180; $t++) {
+    my ($ready) = $t < 60 ? 60 : 0;
+    $load_avg = (59/60) * $load_avg + (1/60) * $ready;
+    $expected[$t] = $load_avg;
+}
+
+mlfqs_compare ("time", "%.2f", \@actual, \@expected, 3.5, [2, 178, 2],
+	       "Some load average values were missing or "
+	       . "differed from those expected "
+	       . "by more than 3.5.");
+pass;
diff --git a/src/tests/threads/mlfqs-load-avg.c b/src/tests/threads/mlfqs-load-avg.c
new file mode 100644
index 0000000..50e83e2
--- /dev/null
+++ b/src/tests/threads/mlfqs-load-avg.c
@@ -0,0 +1,167 @@
+/* Starts 60 threads numbered 0 through 59.  Thread #i sleeps for
+   (10+i) seconds, then spins in a loop for 60 seconds, then
+   sleeps until a total of 120 seconds have passed.  Every 2
+   seconds, starting 10 seconds in, the main thread prints the
+   load average.
+
+   The expected output is listed below.  Some margin of error is
+   allowed.
+
+   If your implementation fails this test but passes most other
+   tests, then consider whether you are doing too much work in
+   the timer interrupt.  If the timer interrupt handler takes too
+   long, then the test's main thread will not have enough time to
+   do its own work (printing a message) and go back to sleep
+   before the next tick arrives.  Then the main thread will be
+   ready, instead of sleeping, when the tick arrives,
+   artificially driving up the load average.
+
+   After 0 seconds, load average=0.00.
+   After 2 seconds, load average=0.05.
+   After 4 seconds, load average=0.16.
+   After 6 seconds, load average=0.34.
+   After 8 seconds, load average=0.58.
+   After 10 seconds, load average=0.87.
+   After 12 seconds, load average=1.22.
+   After 14 seconds, load average=1.63.
+   After 16 seconds, load average=2.09.
+   After 18 seconds, load average=2.60.
+   After 20 seconds, load average=3.16.
+   After 22 seconds, load average=3.76.
+   After 24 seconds, load average=4.42.
+   After 26 seconds, load average=5.11.
+   After 28 seconds, load average=5.85.
+   After 30 seconds, load average=6.63.
+   After 32 seconds, load average=7.46.
+   After 34 seconds, load average=8.32.
+   After 36 seconds, load average=9.22.
+   After 38 seconds, load average=10.15.
+   After 40 seconds, load average=11.12.
+   After 42 seconds, load average=12.13.
+   After 44 seconds, load average=13.16.
+   After 46 seconds, load average=14.23.
+   After 48 seconds, load average=15.33.
+   After 50 seconds, load average=16.46.
+   After 52 seconds, load average=17.62.
+   After 54 seconds, load average=18.81.
+   After 56 seconds, load average=20.02.
+   After 58 seconds, load average=21.26.
+   After 60 seconds, load average=22.52.
+   After 62 seconds, load average=23.71.
+   After 64 seconds, load average=24.80.
+   After 66 seconds, load average=25.78.
+   After 68 seconds, load average=26.66.
+   After 70 seconds, load average=27.45.
+   After 72 seconds, load average=28.14.
+   After 74 seconds, load average=28.75.
+   After 76 seconds, load average=29.27.
+   After 78 seconds, load average=29.71.
+   After 80 seconds, load average=30.06.
+   After 82 seconds, load average=30.34.
+   After 84 seconds, load average=30.55.
+   After 86 seconds, load average=30.68.
+   After 88 seconds, load average=30.74.
+   After 90 seconds, load average=30.73.
+   After 92 seconds, load average=30.66.
+   After 94 seconds, load average=30.52.
+   After 96 seconds, load average=30.32.
+   After 98 seconds, load average=30.06.
+   After 100 seconds, load average=29.74.
+   After 102 seconds, load average=29.37.
+   After 104 seconds, load average=28.95.
+   After 106 seconds, load average=28.47.
+   After 108 seconds, load average=27.94.
+   After 110 seconds, load average=27.36.
+   After 112 seconds, load average=26.74.
+   After 114 seconds, load average=26.07.
+   After 116 seconds, load average=25.36.
+   After 118 seconds, load average=24.60.
+   After 120 seconds, load average=23.81.
+   After 122 seconds, load average=23.02.
+   After 124 seconds, load average=22.26.
+   After 126 seconds, load average=21.52.
+   After 128 seconds, load average=20.81.
+   After 130 seconds, load average=20.12.
+   After 132 seconds, load average=19.46.
+   After 134 seconds, load average=18.81.
+   After 136 seconds, load average=18.19.
+   After 138 seconds, load average=17.59.
+   After 140 seconds, load average=17.01.
+   After 142 seconds, load average=16.45.
+   After 144 seconds, load average=15.90.
+   After 146 seconds, load average=15.38.
+   After 148 seconds, load average=14.87.
+   After 150 seconds, load average=14.38.
+   After 152 seconds, load average=13.90.
+   After 154 seconds, load average=13.44.
+   After 156 seconds, load average=13.00.
+   After 158 seconds, load average=12.57.
+   After 160 seconds, load average=12.15.
+   After 162 seconds, load average=11.75.
+   After 164 seconds, load average=11.36.
+   After 166 seconds, load average=10.99.
+   After 168 seconds, load average=10.62.
+   After 170 seconds, load average=10.27.
+   After 172 seconds, load average=9.93.
+   After 174 seconds, load average=9.61.
+   After 176 seconds, load average=9.29.
+   After 178 seconds, load average=8.98.
+*/
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+static int64_t start_time;
+
+static void load_thread (void *seq_no);
+
+#define THREAD_CNT 60
+
+void
+test_mlfqs_load_avg (void) 
+{
+  int i;
+  
+  ASSERT (thread_mlfqs);
+
+  start_time = timer_ticks ();
+  msg ("Starting %d load threads...", THREAD_CNT);
+  for (i = 0; i < THREAD_CNT; i++) 
+    {
+      char name[16];
+      snprintf(name, sizeof name, "load %d", i);
+      thread_create (name, PRI_DEFAULT, load_thread, (void *) i);
+    }
+  msg ("Starting threads took %d seconds.",
+       timer_elapsed (start_time) / TIMER_FREQ);
+  thread_set_nice (-20);
+
+  for (i = 0; i < 90; i++) 
+    {
+      int64_t sleep_until = start_time + TIMER_FREQ * (2 * i + 10);
+      int load_avg;
+      timer_sleep (sleep_until - timer_ticks ());
+      load_avg = thread_get_load_avg ();
+      msg ("After %d seconds, load average=%d.%02d.",
+           i * 2, load_avg / 100, load_avg % 100);
+    }
+}
+
+static void
+load_thread (void *seq_no_) 
+{
+  int seq_no = (int) seq_no_;
+  int sleep_time = TIMER_FREQ * (10 + seq_no);
+  int spin_time = sleep_time + TIMER_FREQ * THREAD_CNT;
+  int exit_time = TIMER_FREQ * (THREAD_CNT * 2);
+
+  timer_sleep (sleep_time - timer_elapsed (start_time));
+  while (timer_elapsed (start_time) < spin_time)
+    continue;
+  timer_sleep (exit_time - timer_elapsed (start_time));
+}
diff --git a/src/tests/threads/mlfqs-load-avg.ck b/src/tests/threads/mlfqs-load-avg.ck
new file mode 100644
index 0000000..2254d05
--- /dev/null
+++ b/src/tests/threads/mlfqs-load-avg.ck
@@ -0,0 +1,36 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::threads::mlfqs;
+
+our ($test);
+my (@output) = read_text_file ("$test.output");
+
+common_checks ("run", @output);
+@output = get_core_output ("run", @output);
+
+# Get actual values.
+local ($_);
+my (@actual);
+foreach (@output) {
+    my ($t, $load_avg) = /After (\d+) seconds, load average=(\d+\.\d+)\./
+      or next;
+    $actual[$t] = $load_avg;
+}
+
+# Calculate expected values.
+my ($load_avg) = 0;
+my ($recent) = 0;
+my (@expected);
+for (my ($t) = 0; $t < 180; $t++) {
+    my ($ready) = $t < 60 ? $t : $t < 120 ? 120 - $t : 0;
+    $load_avg = (59/60) * $load_avg + (1/60) * $ready;
+    $expected[$t] = $load_avg;
+}
+
+mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2],
+	       "Some load average values were missing or "
+	       . "differed from those expected "
+	       . "by more than 2.5.");
+pass;
diff --git a/src/tests/threads/mlfqs-nice-10.ck b/src/tests/threads/mlfqs-nice-10.ck
new file mode 100644
index 0000000..53e0abe
--- /dev/null
+++ b/src/tests/threads/mlfqs-nice-10.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::threads::mlfqs;
+
+check_mlfqs_fair ([0...9], 25);
diff --git a/src/tests/threads/mlfqs-nice-2.ck b/src/tests/threads/mlfqs-nice-2.ck
new file mode 100644
index 0000000..ada366b
--- /dev/null
+++ b/src/tests/threads/mlfqs-nice-2.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::threads::mlfqs;
+
+check_mlfqs_fair ([0, 5], 50);
diff --git a/src/tests/threads/mlfqs-recent-1.c b/src/tests/threads/mlfqs-recent-1.c
new file mode 100644
index 0000000..4258671
--- /dev/null
+++ b/src/tests/threads/mlfqs-recent-1.c
@@ -0,0 +1,144 @@
+/* Checks that recent_cpu is calculated properly for the case of
+   a single ready process.
+
+   The expected output is this (some margin of error is allowed):
+
+   After 2 seconds, recent_cpu is 6.40, load_avg is 0.03.
+   After 4 seconds, recent_cpu is 12.60, load_avg is 0.07.
+   After 6 seconds, recent_cpu is 18.61, load_avg is 0.10.
+   After 8 seconds, recent_cpu is 24.44, load_avg is 0.13.
+   After 10 seconds, recent_cpu is 30.08, load_avg is 0.15.
+   After 12 seconds, recent_cpu is 35.54, load_avg is 0.18.
+   After 14 seconds, recent_cpu is 40.83, load_avg is 0.21.
+   After 16 seconds, recent_cpu is 45.96, load_avg is 0.24.
+   After 18 seconds, recent_cpu is 50.92, load_avg is 0.26.
+   After 20 seconds, recent_cpu is 55.73, load_avg is 0.29.
+   After 22 seconds, recent_cpu is 60.39, load_avg is 0.31.
+   After 24 seconds, recent_cpu is 64.90, load_avg is 0.33.
+   After 26 seconds, recent_cpu is 69.27, load_avg is 0.35.
+   After 28 seconds, recent_cpu is 73.50, load_avg is 0.38.
+   After 30 seconds, recent_cpu is 77.60, load_avg is 0.40.
+   After 32 seconds, recent_cpu is 81.56, load_avg is 0.42.
+   After 34 seconds, recent_cpu is 85.40, load_avg is 0.44.
+   After 36 seconds, recent_cpu is 89.12, load_avg is 0.45.
+   After 38 seconds, recent_cpu is 92.72, load_avg is 0.47.
+   After 40 seconds, recent_cpu is 96.20, load_avg is 0.49.
+   After 42 seconds, recent_cpu is 99.57, load_avg is 0.51.
+   After 44 seconds, recent_cpu is 102.84, load_avg is 0.52.
+   After 46 seconds, recent_cpu is 106.00, load_avg is 0.54.
+   After 48 seconds, recent_cpu is 109.06, load_avg is 0.55.
+   After 50 seconds, recent_cpu is 112.02, load_avg is 0.57.
+   After 52 seconds, recent_cpu is 114.89, load_avg is 0.58.
+   After 54 seconds, recent_cpu is 117.66, load_avg is 0.60.
+   After 56 seconds, recent_cpu is 120.34, load_avg is 0.61.
+   After 58 seconds, recent_cpu is 122.94, load_avg is 0.62.
+   After 60 seconds, recent_cpu is 125.46, load_avg is 0.64.
+   After 62 seconds, recent_cpu is 127.89, load_avg is 0.65.
+   After 64 seconds, recent_cpu is 130.25, load_avg is 0.66.
+   After 66 seconds, recent_cpu is 132.53, load_avg is 0.67.
+   After 68 seconds, recent_cpu is 134.73, load_avg is 0.68.
+   After 70 seconds, recent_cpu is 136.86, load_avg is 0.69.
+   After 72 seconds, recent_cpu is 138.93, load_avg is 0.70.
+   After 74 seconds, recent_cpu is 140.93, load_avg is 0.71.
+   After 76 seconds, recent_cpu is 142.86, load_avg is 0.72.
+   After 78 seconds, recent_cpu is 144.73, load_avg is 0.73.
+   After 80 seconds, recent_cpu is 146.54, load_avg is 0.74.
+   After 82 seconds, recent_cpu is 148.29, load_avg is 0.75.
+   After 84 seconds, recent_cpu is 149.99, load_avg is 0.76.
+   After 86 seconds, recent_cpu is 151.63, load_avg is 0.76.
+   After 88 seconds, recent_cpu is 153.21, load_avg is 0.77.
+   After 90 seconds, recent_cpu is 154.75, load_avg is 0.78.
+   After 92 seconds, recent_cpu is 156.23, load_avg is 0.79.
+   After 94 seconds, recent_cpu is 157.67, load_avg is 0.79.
+   After 96 seconds, recent_cpu is 159.06, load_avg is 0.80.
+   After 98 seconds, recent_cpu is 160.40, load_avg is 0.81.
+   After 100 seconds, recent_cpu is 161.70, load_avg is 0.81.
+   After 102 seconds, recent_cpu is 162.96, load_avg is 0.82.
+   After 104 seconds, recent_cpu is 164.18, load_avg is 0.83.
+   After 106 seconds, recent_cpu is 165.35, load_avg is 0.83.
+   After 108 seconds, recent_cpu is 166.49, load_avg is 0.84.
+   After 110 seconds, recent_cpu is 167.59, load_avg is 0.84.
+   After 112 seconds, recent_cpu is 168.66, load_avg is 0.85.
+   After 114 seconds, recent_cpu is 169.69, load_avg is 0.85.
+   After 116 seconds, recent_cpu is 170.69, load_avg is 0.86.
+   After 118 seconds, recent_cpu is 171.65, load_avg is 0.86.
+   After 120 seconds, recent_cpu is 172.58, load_avg is 0.87.
+   After 122 seconds, recent_cpu is 173.49, load_avg is 0.87.
+   After 124 seconds, recent_cpu is 174.36, load_avg is 0.88.
+   After 126 seconds, recent_cpu is 175.20, load_avg is 0.88.
+   After 128 seconds, recent_cpu is 176.02, load_avg is 0.88.
+   After 130 seconds, recent_cpu is 176.81, load_avg is 0.89.
+   After 132 seconds, recent_cpu is 177.57, load_avg is 0.89.
+   After 134 seconds, recent_cpu is 178.31, load_avg is 0.89.
+   After 136 seconds, recent_cpu is 179.02, load_avg is 0.90.
+   After 138 seconds, recent_cpu is 179.72, load_avg is 0.90.
+   After 140 seconds, recent_cpu is 180.38, load_avg is 0.90.
+   After 142 seconds, recent_cpu is 181.03, load_avg is 0.91.
+   After 144 seconds, recent_cpu is 181.65, load_avg is 0.91.
+   After 146 seconds, recent_cpu is 182.26, load_avg is 0.91.
+   After 148 seconds, recent_cpu is 182.84, load_avg is 0.92.
+   After 150 seconds, recent_cpu is 183.41, load_avg is 0.92.
+   After 152 seconds, recent_cpu is 183.96, load_avg is 0.92.
+   After 154 seconds, recent_cpu is 184.49, load_avg is 0.92.
+   After 156 seconds, recent_cpu is 185.00, load_avg is 0.93.
+   After 158 seconds, recent_cpu is 185.49, load_avg is 0.93.
+   After 160 seconds, recent_cpu is 185.97, load_avg is 0.93.
+   After 162 seconds, recent_cpu is 186.43, load_avg is 0.93.
+   After 164 seconds, recent_cpu is 186.88, load_avg is 0.94.
+   After 166 seconds, recent_cpu is 187.31, load_avg is 0.94.
+   After 168 seconds, recent_cpu is 187.73, load_avg is 0.94.
+   After 170 seconds, recent_cpu is 188.14, load_avg is 0.94.
+   After 172 seconds, recent_cpu is 188.53, load_avg is 0.94.
+   After 174 seconds, recent_cpu is 188.91, load_avg is 0.95.
+   After 176 seconds, recent_cpu is 189.27, load_avg is 0.95.
+   After 178 seconds, recent_cpu is 189.63, load_avg is 0.95.
+   After 180 seconds, recent_cpu is 189.97, load_avg is 0.95.
+*/   
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+/* Sensitive to assumption that recent_cpu updates happen exactly
+   when timer_ticks() % TIMER_FREQ == 0. */
+
+void
+test_mlfqs_recent_1 (void) 
+{
+  int64_t start_time;
+  int last_elapsed = 0;
+  
+  ASSERT (thread_mlfqs);
+
+  do 
+    {
+      msg ("Sleeping 10 seconds to allow recent_cpu to decay, please wait...");
+      start_time = timer_ticks ();
+      timer_sleep (DIV_ROUND_UP (start_time, TIMER_FREQ) - start_time
+                   + 10 * TIMER_FREQ);
+    }
+  while (thread_get_recent_cpu () > 700);
+
+  start_time = timer_ticks ();
+  for (;;) 
+    {
+      int elapsed = timer_elapsed (start_time);
+      if (elapsed % (TIMER_FREQ * 2) == 0 && elapsed > last_elapsed) 
+        {
+          int recent_cpu = thread_get_recent_cpu ();
+          int load_avg = thread_get_load_avg ();
+          int elapsed_seconds = elapsed / TIMER_FREQ;
+          msg ("After %d seconds, recent_cpu is %d.%02d, load_avg is %d.%02d.",
+               elapsed_seconds,
+               recent_cpu / 100, recent_cpu % 100,
+               load_avg / 100, load_avg % 100);
+          if (elapsed_seconds >= 180)
+            break;
+        } 
+      last_elapsed = elapsed;
+    }
+}
diff --git a/src/tests/threads/mlfqs-recent-1.ck b/src/tests/threads/mlfqs-recent-1.ck
new file mode 100644
index 0000000..a2ba44d
--- /dev/null
+++ b/src/tests/threads/mlfqs-recent-1.ck
@@ -0,0 +1,31 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::threads::mlfqs;
+
+our ($test);
+my (@output) = read_text_file ("$test.output");
+common_checks ("run", @output);
+@output = get_core_output ("run", @output);
+
+# Get actual values.
+local ($_);
+my (@actual);
+foreach (@output) {
+    my ($t, $recent_cpu) = /After (\d+) seconds, recent_cpu is (\d+\.\d+),/
+      or next;
+    $actual[$t] = $recent_cpu;
+}
+
+# Calculate expected values.
+my ($expected_load_avg, $expected_recent_cpu)
+  = mlfqs_expected_load ([(1) x 180], [(100) x 180]);
+my (@expected) = @$expected_recent_cpu;
+
+# Compare actual and expected values.
+mlfqs_compare ("time", "%.2f", \@actual, \@expected, 2.5, [2, 178, 2],
+	       "Some recent_cpu values were missing or "
+	       . "differed from those expected "
+	       . "by more than 2.5.");
+pass;
diff --git a/src/tests/threads/mlfqs.pm b/src/tests/threads/mlfqs.pm
new file mode 100644
index 0000000..184ac16
--- /dev/null
+++ b/src/tests/threads/mlfqs.pm
@@ -0,0 +1,146 @@
+# -*- perl -*-
+use strict;
+use warnings;
+
+sub mlfqs_expected_load {
+    my ($ready, $recent_delta) = @_;
+    my (@load_avg) = 0;
+    my (@recent_cpu) = 0;
+    my ($load_avg) = 0;
+    my ($recent_cpu) = 0;
+    for my $i (0...$#$ready) {
+	$load_avg = (59/60) * $load_avg + (1/60) * $ready->[$i];
+	push (@load_avg, $load_avg);
+
+	if (defined $recent_delta->[$i]) {
+	    my ($twice_load) = $load_avg * 2;
+	    my ($load_factor) = $twice_load / ($twice_load + 1);
+	    $recent_cpu = ($recent_cpu + $recent_delta->[$i]) * $load_factor;
+	    push (@recent_cpu, $recent_cpu);
+	}
+    }
+    return (\@load_avg, \@recent_cpu);
+}
+
+sub mlfqs_expected_ticks {
+    my (@nice) = @_;
+    my ($thread_cnt) = scalar (@nice);
+    my (@recent_cpu) = (0) x $thread_cnt;
+    my (@slices) = (0) x $thread_cnt;
+    my (@fifo) = (0) x $thread_cnt;
+    my ($next_fifo) = 1;
+    my ($load_avg) = 0;
+    for my $i (1...750) {
+	if ($i % 25 == 0) {
+	    # Update load average.
+	    $load_avg = (59/60) * $load_avg + (1/60) * $thread_cnt;
+
+	    # Update recent_cpu.
+	    my ($twice_load) = $load_avg * 2;
+	    my ($load_factor) = $twice_load / ($twice_load + 1);
+	    $recent_cpu[$_] = $recent_cpu[$_] * $load_factor + $nice[$_]
+	      foreach 0...($thread_cnt - 1);
+	}
+
+	# Update priorities.
+	my (@priority);
+	foreach my $j (0...($thread_cnt - 1)) {
+	    my ($priority) = int ($recent_cpu[$j] / 4 + $nice[$j] * 2);
+	    $priority = 0 if $priority < 0;
+	    $priority = 63 if $priority > 63;
+	    push (@priority, $priority);
+	}
+
+	# Choose thread to run.
+	my $max = 0;
+	for my $j (1...$#priority) {
+	    if ($priority[$j] < $priority[$max]
+		|| ($priority[$j] == $priority[$max]
+		    && $fifo[$j] < $fifo[$max])) {
+		$max = $j;
+	    }
+	}
+	$fifo[$max] = $next_fifo++;
+
+	# Run thread.
+	$recent_cpu[$max] += 4;
+	$slices[$max] += 4;
+    }
+    return @slices;
+}
+
+sub check_mlfqs_fair {
+    my ($nice, $maxdiff) = @_;
+    our ($test);
+    my (@output) = read_text_file ("$test.output");
+    common_checks ("run", @output);
+    @output = get_core_output ("run", @output);
+
+    my (@actual);
+    local ($_);
+    foreach (@output) {
+	my ($id, $count) = /Thread (\d+) received (\d+) ticks\./ or next;
+        $actual[$id] = $count;
+    }
+
+    my (@expected) = mlfqs_expected_ticks (@$nice);
+    mlfqs_compare ("thread", "%d",
+		   \@actual, \@expected, $maxdiff, [0, $#$nice, 1],
+		   "Some tick counts were missing or differed from those "
+		   . "expected by more than $maxdiff.");
+    pass;
+}
+
+sub mlfqs_compare {
+    my ($indep_var, $format,
+	$actual_ref, $expected_ref, $maxdiff, $t_range, $message) = @_;
+    my ($t_min, $t_max, $t_step) = @$t_range;
+
+    my ($ok) = 1;
+    for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) {
+	my ($actual) = $actual_ref->[$t];
+	my ($expected) = $expected_ref->[$t];
+	$ok = 0, last
+	  if !defined ($actual) || abs ($actual - $expected) > $maxdiff + .01;
+    }
+    return if $ok;
+
+    print "$message\n";
+    mlfqs_row ($indep_var, "actual", "<->", "expected", "explanation");
+    mlfqs_row ("------", "--------", "---", "--------", '-' x 40);
+    for (my ($t) = $t_min; $t <= $t_max; $t += $t_step) {
+	my ($actual) = $actual_ref->[$t];
+	my ($expected) = $expected_ref->[$t];
+	my ($diff, $rationale);
+	if (!defined $actual) {
+	    $actual = 'undef' ;
+	    $diff = '';
+	    $rationale = 'Missing value.';
+	} else {
+	    my ($delta) = abs ($actual - $expected);
+	    if ($delta > $maxdiff + .01) {
+		my ($excess) = $delta - $maxdiff;
+		if ($actual > $expected) {
+		    $diff = '>>>';
+		    $rationale = sprintf "Too big, by $format.", $excess;
+		} else {
+		    $diff = '<<<';
+		    $rationale = sprintf "Too small, by $format.", $excess;
+		}
+	    } else {
+		$diff = ' = ';
+		$rationale = '';
+	    }
+	    $actual = sprintf ($format, $actual);
+	}
+	$expected = sprintf ($format, $expected);
+	mlfqs_row ($t, $actual, $diff, $expected, $rationale);
+    }
+    fail;
+}
+
+sub mlfqs_row {
+    printf "%6s %8s %3s %-8s %s\n", @_;
+}
+
+1;
diff --git a/src/tests/threads/priority-change.c b/src/tests/threads/priority-change.c
new file mode 100644
index 0000000..810b05a
--- /dev/null
+++ b/src/tests/threads/priority-change.c
@@ -0,0 +1,31 @@
+/* Verifies that lowering a thread's priority so that it is no
+   longer the highest-priority thread in the system causes it to
+   yield immediately. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/thread.h"
+
+static thread_func changing_thread;
+
+void
+test_priority_change (void) 
+{
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  msg ("Creating a high-priority thread 2.");
+  thread_create ("thread 2", PRI_DEFAULT + 1, changing_thread, NULL);
+  msg ("Thread 2 should have just lowered its priority.");
+  thread_set_priority (PRI_DEFAULT - 2);
+  msg ("Thread 2 should have just exited.");
+}
+
+static void
+changing_thread (void *aux UNUSED) 
+{
+  msg ("Thread 2 now lowering priority.");
+  thread_set_priority (PRI_DEFAULT - 1);
+  msg ("Thread 2 exiting.");
+}
diff --git a/src/tests/threads/priority-change.ck b/src/tests/threads/priority-change.ck
new file mode 100644
index 0000000..f4d9b2f
--- /dev/null
+++ b/src/tests/threads/priority-change.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-change) begin
+(priority-change) Creating a high-priority thread 2.
+(priority-change) Thread 2 now lowering priority.
+(priority-change) Thread 2 should have just lowered its priority.
+(priority-change) Thread 2 exiting.
+(priority-change) Thread 2 should have just exited.
+(priority-change) end
+EOF
+pass;
diff --git a/src/tests/threads/priority-condvar.c b/src/tests/threads/priority-condvar.c
new file mode 100644
index 0000000..c1efb1b
--- /dev/null
+++ b/src/tests/threads/priority-condvar.c
@@ -0,0 +1,53 @@
+/* Tests that cond_signal() wakes up the highest-priority thread
+   waiting in cond_wait(). */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+static thread_func priority_condvar_thread;
+static struct lock lock;
+static struct condition condition;
+
+void
+test_priority_condvar (void) 
+{
+  int i;
+  
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  lock_init (&lock);
+  cond_init (&condition);
+
+  thread_set_priority (PRI_MIN);
+  for (i = 0; i < 10; i++) 
+    {
+      int priority = PRI_DEFAULT - (i + 7) % 10 - 1;
+      char name[16];
+      snprintf (name, sizeof name, "priority %d", priority);
+      thread_create (name, priority, priority_condvar_thread, NULL);
+    }
+
+  for (i = 0; i < 10; i++) 
+    {
+      lock_acquire (&lock);
+      msg ("Signaling...");
+      cond_signal (&condition, &lock);
+      lock_release (&lock);
+    }
+}
+
+static void
+priority_condvar_thread (void *aux UNUSED) 
+{
+  msg ("Thread %s starting.", thread_name ());
+  lock_acquire (&lock);
+  cond_wait (&condition, &lock);
+  msg ("Thread %s woke up.", thread_name ());
+  lock_release (&lock);
+}
diff --git a/src/tests/threads/priority-condvar.ck b/src/tests/threads/priority-condvar.ck
new file mode 100644
index 0000000..195c1ab
--- /dev/null
+++ b/src/tests/threads/priority-condvar.ck
@@ -0,0 +1,39 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-condvar) begin
+(priority-condvar) Thread priority 23 starting.
+(priority-condvar) Thread priority 22 starting.
+(priority-condvar) Thread priority 21 starting.
+(priority-condvar) Thread priority 30 starting.
+(priority-condvar) Thread priority 29 starting.
+(priority-condvar) Thread priority 28 starting.
+(priority-condvar) Thread priority 27 starting.
+(priority-condvar) Thread priority 26 starting.
+(priority-condvar) Thread priority 25 starting.
+(priority-condvar) Thread priority 24 starting.
+(priority-condvar) Signaling...
+(priority-condvar) Thread priority 30 woke up.
+(priority-condvar) Signaling...
+(priority-condvar) Thread priority 29 woke up.
+(priority-condvar) Signaling...
+(priority-condvar) Thread priority 28 woke up.
+(priority-condvar) Signaling...
+(priority-condvar) Thread priority 27 woke up.
+(priority-condvar) Signaling...
+(priority-condvar) Thread priority 26 woke up.
+(priority-condvar) Signaling...
+(priority-condvar) Thread priority 25 woke up.
+(priority-condvar) Signaling...
+(priority-condvar) Thread priority 24 woke up.
+(priority-condvar) Signaling...
+(priority-condvar) Thread priority 23 woke up.
+(priority-condvar) Signaling...
+(priority-condvar) Thread priority 22 woke up.
+(priority-condvar) Signaling...
+(priority-condvar) Thread priority 21 woke up.
+(priority-condvar) end
+EOF
+pass;
diff --git a/src/tests/threads/priority-donate-chain.c b/src/tests/threads/priority-donate-chain.c
new file mode 100644
index 0000000..3ffabca
--- /dev/null
+++ b/src/tests/threads/priority-donate-chain.c
@@ -0,0 +1,114 @@
+/* The main thread set its priority to PRI_MIN and creates 7 threads 
+   (thread 1..7) with priorities PRI_MIN + 3, 6, 9, 12, ...
+   The main thread initializes 8 locks: lock 0..7 and acquires lock 0.
+
+   When thread[i] starts, it first acquires lock[i] (unless i == 7.)
+   Subsequently, thread[i] attempts to acquire lock[i-1], which is held by
+   thread[i-1], except for lock[0], which is held by the main thread.
+   Because the lock is held, thread[i] donates its priority to thread[i-1],
+   which donates to thread[i-2], and so on until the main thread
+   receives the donation.
+
+   After threads[1..7] have been created and are blocked on locks[0..7],
+   the main thread releases lock[0], unblocking thread[1], and being
+   preempted by it.
+   Thread[1] then completes acquiring lock[0], then releases lock[0],
+   then releases lock[1], unblocking thread[2], etc.
+   Thread[7] finally acquires & releases lock[7] and exits, allowing 
+   thread[6], then thread[5] etc. to run and exit until finally the 
+   main thread exits.
+
+   In addition, interloper threads are created at priority levels
+   p = PRI_MIN + 2, 5, 8, 11, ... which should not be run until the 
+   corresponding thread with priority p + 1 has finished.
+  
+   Written by Godmar Back <gback@cs.vt.edu> */ 
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+#define NESTING_DEPTH 8
+
+struct lock_pair
+  {
+    struct lock *second;
+    struct lock *first;
+  };
+
+static thread_func donor_thread_func;
+static thread_func interloper_thread_func;
+
+void
+test_priority_donate_chain (void) 
+{
+  int i;  
+  struct lock locks[NESTING_DEPTH - 1];
+  struct lock_pair lock_pairs[NESTING_DEPTH];
+
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  thread_set_priority (PRI_MIN);
+
+  for (i = 0; i < NESTING_DEPTH - 1; i++)
+    lock_init (&locks[i]);
+
+  lock_acquire (&locks[0]);
+  msg ("%s got lock.", thread_name ());
+
+  for (i = 1; i < NESTING_DEPTH; i++)
+    {
+      char name[16];
+      int thread_priority;
+
+      snprintf (name, sizeof name, "thread %d", i);
+      thread_priority = PRI_MIN + i * 3;
+      lock_pairs[i].first = i < NESTING_DEPTH - 1 ? locks + i: NULL;
+      lock_pairs[i].second = locks + i - 1;
+
+      thread_create (name, thread_priority, donor_thread_func, lock_pairs + i);
+      msg ("%s should have priority %d.  Actual priority: %d.",
+          thread_name (), thread_priority, thread_get_priority ());
+
+      snprintf (name, sizeof name, "interloper %d", i);
+      thread_create (name, thread_priority - 1, interloper_thread_func, NULL);
+    }
+
+  lock_release (&locks[0]);
+  msg ("%s finishing with priority %d.", thread_name (),
+                                         thread_get_priority ());
+}
+
+static void
+donor_thread_func (void *locks_) 
+{
+  struct lock_pair *locks = locks_;
+
+  if (locks->first)
+    lock_acquire (locks->first);
+
+  lock_acquire (locks->second);
+  msg ("%s got lock", thread_name ());
+
+  lock_release (locks->second);
+  msg ("%s should have priority %d. Actual priority: %d", 
+        thread_name (), (NESTING_DEPTH - 1) * 3,
+        thread_get_priority ());
+
+  if (locks->first)
+    lock_release (locks->first);
+
+  msg ("%s finishing with priority %d.", thread_name (),
+                                         thread_get_priority ());
+}
+
+static void
+interloper_thread_func (void *arg_ UNUSED)
+{
+  msg ("%s finished.", thread_name ());
+}
+
+// vim: sw=2
diff --git a/src/tests/threads/priority-donate-chain.ck b/src/tests/threads/priority-donate-chain.ck
new file mode 100644
index 0000000..213e649
--- /dev/null
+++ b/src/tests/threads/priority-donate-chain.ck
@@ -0,0 +1,46 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-donate-chain) begin
+(priority-donate-chain) main got lock.
+(priority-donate-chain) main should have priority 3.  Actual priority: 3.
+(priority-donate-chain) main should have priority 6.  Actual priority: 6.
+(priority-donate-chain) main should have priority 9.  Actual priority: 9.
+(priority-donate-chain) main should have priority 12.  Actual priority: 12.
+(priority-donate-chain) main should have priority 15.  Actual priority: 15.
+(priority-donate-chain) main should have priority 18.  Actual priority: 18.
+(priority-donate-chain) main should have priority 21.  Actual priority: 21.
+(priority-donate-chain) thread 1 got lock
+(priority-donate-chain) thread 1 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 2 got lock
+(priority-donate-chain) thread 2 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 3 got lock
+(priority-donate-chain) thread 3 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 4 got lock
+(priority-donate-chain) thread 4 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 5 got lock
+(priority-donate-chain) thread 5 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 6 got lock
+(priority-donate-chain) thread 6 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 7 got lock
+(priority-donate-chain) thread 7 should have priority 21. Actual priority: 21
+(priority-donate-chain) thread 7 finishing with priority 21.
+(priority-donate-chain) interloper 7 finished.
+(priority-donate-chain) thread 6 finishing with priority 18.
+(priority-donate-chain) interloper 6 finished.
+(priority-donate-chain) thread 5 finishing with priority 15.
+(priority-donate-chain) interloper 5 finished.
+(priority-donate-chain) thread 4 finishing with priority 12.
+(priority-donate-chain) interloper 4 finished.
+(priority-donate-chain) thread 3 finishing with priority 9.
+(priority-donate-chain) interloper 3 finished.
+(priority-donate-chain) thread 2 finishing with priority 6.
+(priority-donate-chain) interloper 2 finished.
+(priority-donate-chain) thread 1 finishing with priority 3.
+(priority-donate-chain) interloper 1 finished.
+(priority-donate-chain) main finishing with priority 0.
+(priority-donate-chain) end
+EOF
+pass;
diff --git a/src/tests/threads/priority-donate-lower.c b/src/tests/threads/priority-donate-lower.c
new file mode 100644
index 0000000..4965d75
--- /dev/null
+++ b/src/tests/threads/priority-donate-lower.c
@@ -0,0 +1,51 @@
+/* The main thread acquires a lock.  Then it creates a
+   higher-priority thread that blocks acquiring the lock, causing
+   it to donate their priorities to the main thread.  The main
+   thread attempts to lower its priority, which should not take
+   effect until the donation is released. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+static thread_func acquire_thread_func;
+
+void
+test_priority_donate_lower (void) 
+{
+  struct lock lock;
+
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  /* Make sure our priority is the default. */
+  ASSERT (thread_get_priority () == PRI_DEFAULT);
+
+  lock_init (&lock);
+  lock_acquire (&lock);
+  thread_create ("acquire", PRI_DEFAULT + 10, acquire_thread_func, &lock);
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 10, thread_get_priority ());
+
+  msg ("Lowering base priority...");
+  thread_set_priority (PRI_DEFAULT - 10);
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 10, thread_get_priority ());
+  lock_release (&lock);
+  msg ("acquire must already have finished.");
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT - 10, thread_get_priority ());
+}
+
+static void
+acquire_thread_func (void *lock_) 
+{
+  struct lock *lock = lock_;
+
+  lock_acquire (lock);
+  msg ("acquire: got the lock");
+  lock_release (lock);
+  msg ("acquire: done");
+}
diff --git a/src/tests/threads/priority-donate-lower.ck b/src/tests/threads/priority-donate-lower.ck
new file mode 100644
index 0000000..c9bb61b
--- /dev/null
+++ b/src/tests/threads/priority-donate-lower.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-donate-lower) begin
+(priority-donate-lower) Main thread should have priority 41.  Actual priority: 41.
+(priority-donate-lower) Lowering base priority...
+(priority-donate-lower) Main thread should have priority 41.  Actual priority: 41.
+(priority-donate-lower) acquire: got the lock
+(priority-donate-lower) acquire: done
+(priority-donate-lower) acquire must already have finished.
+(priority-donate-lower) Main thread should have priority 21.  Actual priority: 21.
+(priority-donate-lower) end
+EOF
+pass;
diff --git a/src/tests/threads/priority-donate-multiple.c b/src/tests/threads/priority-donate-multiple.c
new file mode 100644
index 0000000..df4689c
--- /dev/null
+++ b/src/tests/threads/priority-donate-multiple.c
@@ -0,0 +1,77 @@
+/* The main thread acquires locks A and B, then it creates two
+   higher-priority threads.  Each of these threads blocks
+   acquiring one of the locks and thus donate their priority to
+   the main thread.  The main thread releases the locks in turn
+   and relinquishes its donated priorities.
+   
+   Based on a test originally submitted for Stanford's CS 140 in
+   winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
+   Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
+   <yph@cs.stanford.edu>.  Modified by arens. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+static thread_func a_thread_func;
+static thread_func b_thread_func;
+
+void
+test_priority_donate_multiple (void) 
+{
+  struct lock a, b;
+
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  /* Make sure our priority is the default. */
+  ASSERT (thread_get_priority () == PRI_DEFAULT);
+
+  lock_init (&a);
+  lock_init (&b);
+
+  lock_acquire (&a);
+  lock_acquire (&b);
+
+  thread_create ("a", PRI_DEFAULT + 1, a_thread_func, &a);
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 1, thread_get_priority ());
+
+  thread_create ("b", PRI_DEFAULT + 2, b_thread_func, &b);
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 2, thread_get_priority ());
+
+  lock_release (&b);
+  msg ("Thread b should have just finished.");
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 1, thread_get_priority ());
+
+  lock_release (&a);
+  msg ("Thread a should have just finished.");
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT, thread_get_priority ());
+}
+
+static void
+a_thread_func (void *lock_) 
+{
+  struct lock *lock = lock_;
+
+  lock_acquire (lock);
+  msg ("Thread a acquired lock a.");
+  lock_release (lock);
+  msg ("Thread a finished.");
+}
+
+static void
+b_thread_func (void *lock_) 
+{
+  struct lock *lock = lock_;
+
+  lock_acquire (lock);
+  msg ("Thread b acquired lock b.");
+  lock_release (lock);
+  msg ("Thread b finished.");
+}
diff --git a/src/tests/threads/priority-donate-multiple.ck b/src/tests/threads/priority-donate-multiple.ck
new file mode 100644
index 0000000..0afd20b
--- /dev/null
+++ b/src/tests/threads/priority-donate-multiple.ck
@@ -0,0 +1,19 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-donate-multiple) begin
+(priority-donate-multiple) Main thread should have priority 32.  Actual priority: 32.
+(priority-donate-multiple) Main thread should have priority 33.  Actual priority: 33.
+(priority-donate-multiple) Thread b acquired lock b.
+(priority-donate-multiple) Thread b finished.
+(priority-donate-multiple) Thread b should have just finished.
+(priority-donate-multiple) Main thread should have priority 32.  Actual priority: 32.
+(priority-donate-multiple) Thread a acquired lock a.
+(priority-donate-multiple) Thread a finished.
+(priority-donate-multiple) Thread a should have just finished.
+(priority-donate-multiple) Main thread should have priority 31.  Actual priority: 31.
+(priority-donate-multiple) end
+EOF
+pass;
diff --git a/src/tests/threads/priority-donate-multiple2.c b/src/tests/threads/priority-donate-multiple2.c
new file mode 100644
index 0000000..7f65fef
--- /dev/null
+++ b/src/tests/threads/priority-donate-multiple2.c
@@ -0,0 +1,90 @@
+/* The main thread acquires locks A and B, then it creates three
+   higher-priority threads.  The first two of these threads block
+   acquiring one of the locks and thus donate their priority to
+   the main thread.  The main thread releases the locks in turn
+   and relinquishes its donated priorities, allowing the third thread
+   to run.
+
+   In this test, the main thread releases the locks in a different
+   order compared to priority-donate-multiple.c.
+   
+   Written by Godmar Back <gback@cs.vt.edu>. 
+   Based on a test originally submitted for Stanford's CS 140 in
+   winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
+   Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
+   <yph@cs.stanford.edu>.  Modified by arens. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+static thread_func a_thread_func;
+static thread_func b_thread_func;
+static thread_func c_thread_func;
+
+void
+test_priority_donate_multiple2 (void) 
+{
+  struct lock a, b;
+
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  /* Make sure our priority is the default. */
+  ASSERT (thread_get_priority () == PRI_DEFAULT);
+
+  lock_init (&a);
+  lock_init (&b);
+
+  lock_acquire (&a);
+  lock_acquire (&b);
+
+  thread_create ("a", PRI_DEFAULT + 3, a_thread_func, &a);
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 3, thread_get_priority ());
+
+  thread_create ("c", PRI_DEFAULT + 1, c_thread_func, NULL);
+
+  thread_create ("b", PRI_DEFAULT + 5, b_thread_func, &b);
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 5, thread_get_priority ());
+
+  lock_release (&a);
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 5, thread_get_priority ());
+
+  lock_release (&b);
+  msg ("Threads b, a, c should have just finished, in that order.");
+  msg ("Main thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT, thread_get_priority ());
+}
+
+static void
+a_thread_func (void *lock_) 
+{
+  struct lock *lock = lock_;
+
+  lock_acquire (lock);
+  msg ("Thread a acquired lock a.");
+  lock_release (lock);
+  msg ("Thread a finished.");
+}
+
+static void
+b_thread_func (void *lock_) 
+{
+  struct lock *lock = lock_;
+
+  lock_acquire (lock);
+  msg ("Thread b acquired lock b.");
+  lock_release (lock);
+  msg ("Thread b finished.");
+}
+
+static void
+c_thread_func (void *a_ UNUSED) 
+{
+  msg ("Thread c finished.");
+}
diff --git a/src/tests/threads/priority-donate-multiple2.ck b/src/tests/threads/priority-donate-multiple2.ck
new file mode 100644
index 0000000..b23533a
--- /dev/null
+++ b/src/tests/threads/priority-donate-multiple2.ck
@@ -0,0 +1,19 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-donate-multiple2) begin
+(priority-donate-multiple2) Main thread should have priority 34.  Actual priority: 34.
+(priority-donate-multiple2) Main thread should have priority 36.  Actual priority: 36.
+(priority-donate-multiple2) Main thread should have priority 36.  Actual priority: 36.
+(priority-donate-multiple2) Thread b acquired lock b.
+(priority-donate-multiple2) Thread b finished.
+(priority-donate-multiple2) Thread a acquired lock a.
+(priority-donate-multiple2) Thread a finished.
+(priority-donate-multiple2) Thread c finished.
+(priority-donate-multiple2) Threads b, a, c should have just finished, in that order.
+(priority-donate-multiple2) Main thread should have priority 31.  Actual priority: 31.
+(priority-donate-multiple2) end
+EOF
+pass;
diff --git a/src/tests/threads/priority-donate-nest.c b/src/tests/threads/priority-donate-nest.c
new file mode 100644
index 0000000..3a3a9a5
--- /dev/null
+++ b/src/tests/threads/priority-donate-nest.c
@@ -0,0 +1,94 @@
+/* Low-priority main thread L acquires lock A.  Medium-priority
+   thread M then acquires lock B then blocks on acquiring lock A.
+   High-priority thread H then blocks on acquiring lock B.  Thus,
+   thread H donates its priority to M, which in turn donates it
+   to thread L.
+   
+   Based on a test originally submitted for Stanford's CS 140 in
+   winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
+   Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
+   <yph@cs.stanford.edu>.  Modified by arens. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+struct locks 
+  {
+    struct lock *a;
+    struct lock *b;
+  };
+
+static thread_func medium_thread_func;
+static thread_func high_thread_func;
+
+void
+test_priority_donate_nest (void) 
+{
+  struct lock a, b;
+  struct locks locks;
+
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  /* Make sure our priority is the default. */
+  ASSERT (thread_get_priority () == PRI_DEFAULT);
+
+  lock_init (&a);
+  lock_init (&b);
+
+  lock_acquire (&a);
+
+  locks.a = &a;
+  locks.b = &b;
+  thread_create ("medium", PRI_DEFAULT + 1, medium_thread_func, &locks);
+  thread_yield ();
+  msg ("Low thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 1, thread_get_priority ());
+
+  thread_create ("high", PRI_DEFAULT + 2, high_thread_func, &b);
+  thread_yield ();
+  msg ("Low thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 2, thread_get_priority ());
+
+  lock_release (&a);
+  thread_yield ();
+  msg ("Medium thread should just have finished.");
+  msg ("Low thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT, thread_get_priority ());
+}
+
+static void
+medium_thread_func (void *locks_) 
+{
+  struct locks *locks = locks_;
+
+  lock_acquire (locks->b);
+  lock_acquire (locks->a);
+
+  msg ("Medium thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 2, thread_get_priority ());
+  msg ("Medium thread got the lock.");
+
+  lock_release (locks->a);
+  thread_yield ();
+
+  lock_release (locks->b);
+  thread_yield ();
+
+  msg ("High thread should have just finished.");
+  msg ("Middle thread finished.");
+}
+
+static void
+high_thread_func (void *lock_) 
+{
+  struct lock *lock = lock_;
+
+  lock_acquire (lock);
+  msg ("High thread got the lock.");
+  lock_release (lock);
+  msg ("High thread finished.");
+}
diff --git a/src/tests/threads/priority-donate-nest.ck b/src/tests/threads/priority-donate-nest.ck
new file mode 100644
index 0000000..923460e
--- /dev/null
+++ b/src/tests/threads/priority-donate-nest.ck
@@ -0,0 +1,19 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-donate-nest) begin
+(priority-donate-nest) Low thread should have priority 32.  Actual priority: 32.
+(priority-donate-nest) Low thread should have priority 33.  Actual priority: 33.
+(priority-donate-nest) Medium thread should have priority 33.  Actual priority: 33.
+(priority-donate-nest) Medium thread got the lock.
+(priority-donate-nest) High thread got the lock.
+(priority-donate-nest) High thread finished.
+(priority-donate-nest) High thread should have just finished.
+(priority-donate-nest) Middle thread finished.
+(priority-donate-nest) Medium thread should just have finished.
+(priority-donate-nest) Low thread should have priority 31.  Actual priority: 31.
+(priority-donate-nest) end
+EOF
+pass;
diff --git a/src/tests/threads/priority-donate-one.c b/src/tests/threads/priority-donate-one.c
new file mode 100644
index 0000000..3189f3a
--- /dev/null
+++ b/src/tests/threads/priority-donate-one.c
@@ -0,0 +1,65 @@
+/* The main thread acquires a lock.  Then it creates two
+   higher-priority threads that block acquiring the lock, causing
+   them to donate their priorities to the main thread.  When the
+   main thread releases the lock, the other threads should
+   acquire it in priority order.
+
+   Based on a test originally submitted for Stanford's CS 140 in
+   winter 1999 by Matt Franklin <startled@leland.stanford.edu>,
+   Greg Hutchins <gmh@leland.stanford.edu>, Yu Ping Hu
+   <yph@cs.stanford.edu>.  Modified by arens. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+static thread_func acquire1_thread_func;
+static thread_func acquire2_thread_func;
+
+void
+test_priority_donate_one (void) 
+{
+  struct lock lock;
+
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  /* Make sure our priority is the default. */
+  ASSERT (thread_get_priority () == PRI_DEFAULT);
+
+  lock_init (&lock);
+  lock_acquire (&lock);
+  thread_create ("acquire1", PRI_DEFAULT + 1, acquire1_thread_func, &lock);
+  msg ("This thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 1, thread_get_priority ());
+  thread_create ("acquire2", PRI_DEFAULT + 2, acquire2_thread_func, &lock);
+  msg ("This thread should have priority %d.  Actual priority: %d.",
+       PRI_DEFAULT + 2, thread_get_priority ());
+  lock_release (&lock);
+  msg ("acquire2, acquire1 must already have finished, in that order.");
+  msg ("This should be the last line before finishing this test.");
+}
+
+static void
+acquire1_thread_func (void *lock_) 
+{
+  struct lock *lock = lock_;
+
+  lock_acquire (lock);
+  msg ("acquire1: got the lock");
+  lock_release (lock);
+  msg ("acquire1: done");
+}
+
+static void
+acquire2_thread_func (void *lock_) 
+{
+  struct lock *lock = lock_;
+
+  lock_acquire (lock);
+  msg ("acquire2: got the lock");
+  lock_release (lock);
+  msg ("acquire2: done");
+}
diff --git a/src/tests/threads/priority-donate-one.ck b/src/tests/threads/priority-donate-one.ck
new file mode 100644
index 0000000..b7c8e6f
--- /dev/null
+++ b/src/tests/threads/priority-donate-one.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-donate-one) begin
+(priority-donate-one) This thread should have priority 32.  Actual priority: 32.
+(priority-donate-one) This thread should have priority 33.  Actual priority: 33.
+(priority-donate-one) acquire2: got the lock
+(priority-donate-one) acquire2: done
+(priority-donate-one) acquire1: got the lock
+(priority-donate-one) acquire1: done
+(priority-donate-one) acquire2, acquire1 must already have finished, in that order.
+(priority-donate-one) This should be the last line before finishing this test.
+(priority-donate-one) end
+EOF
+pass;
diff --git a/src/tests/threads/priority-donate-sema.c b/src/tests/threads/priority-donate-sema.c
new file mode 100644
index 0000000..b33cb72
--- /dev/null
+++ b/src/tests/threads/priority-donate-sema.c
@@ -0,0 +1,82 @@
+/* Low priority thread L acquires a lock, then blocks downing a
+   semaphore.  Medium priority thread M then blocks waiting on
+   the same semaphore.  Next, high priority thread H attempts to
+   acquire the lock, donating its priority to L.
+
+   Next, the main thread ups the semaphore, waking up L.  L
+   releases the lock, which wakes up H.  H "up"s the semaphore,
+   waking up M.  H terminates, then M, then L, and finally the
+   main thread.
+
+   Written by Godmar Back <gback@cs.vt.edu>. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+struct lock_and_sema 
+  {
+    struct lock lock;
+    struct semaphore sema;
+  };
+
+static thread_func l_thread_func;
+static thread_func m_thread_func;
+static thread_func h_thread_func;
+
+void
+test_priority_donate_sema (void) 
+{
+  struct lock_and_sema ls;
+
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  /* Make sure our priority is the default. */
+  ASSERT (thread_get_priority () == PRI_DEFAULT);
+
+  lock_init (&ls.lock);
+  sema_init (&ls.sema, 0);
+  thread_create ("low", PRI_DEFAULT + 1, l_thread_func, &ls);
+  thread_create ("med", PRI_DEFAULT + 3, m_thread_func, &ls);
+  thread_create ("high", PRI_DEFAULT + 5, h_thread_func, &ls);
+  sema_up (&ls.sema);
+  msg ("Main thread finished.");
+}
+
+static void
+l_thread_func (void *ls_) 
+{
+  struct lock_and_sema *ls = ls_;
+
+  lock_acquire (&ls->lock);
+  msg ("Thread L acquired lock.");
+  sema_down (&ls->sema);
+  msg ("Thread L downed semaphore.");
+  lock_release (&ls->lock);
+  msg ("Thread L finished.");
+}
+
+static void
+m_thread_func (void *ls_) 
+{
+  struct lock_and_sema *ls = ls_;
+
+  sema_down (&ls->sema);
+  msg ("Thread M finished.");
+}
+
+static void
+h_thread_func (void *ls_) 
+{
+  struct lock_and_sema *ls = ls_;
+
+  lock_acquire (&ls->lock);
+  msg ("Thread H acquired lock.");
+
+  sema_up (&ls->sema);
+  lock_release (&ls->lock);
+  msg ("Thread H finished.");
+}
diff --git a/src/tests/threads/priority-donate-sema.ck b/src/tests/threads/priority-donate-sema.ck
new file mode 100644
index 0000000..92b8d07
--- /dev/null
+++ b/src/tests/threads/priority-donate-sema.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-donate-sema) begin
+(priority-donate-sema) Thread L acquired lock.
+(priority-donate-sema) Thread L downed semaphore.
+(priority-donate-sema) Thread H acquired lock.
+(priority-donate-sema) Thread H finished.
+(priority-donate-sema) Thread M finished.
+(priority-donate-sema) Thread L finished.
+(priority-donate-sema) Main thread finished.
+(priority-donate-sema) end
+EOF
+pass;
diff --git a/src/tests/threads/priority-fifo.c b/src/tests/threads/priority-fifo.c
new file mode 100644
index 0000000..3af98a3
--- /dev/null
+++ b/src/tests/threads/priority-fifo.c
@@ -0,0 +1,99 @@
+/* Creates several threads all at the same priority and ensures
+   that they consistently run in the same round-robin order.
+
+   Based on a test originally submitted for Stanford's CS 140 in
+   winter 1999 by by Matt Franklin
+   <startled@leland.stanford.edu>, Greg Hutchins
+   <gmh@leland.stanford.edu>, Yu Ping Hu <yph@cs.stanford.edu>.
+   Modified by arens. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "devices/timer.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+struct simple_thread_data 
+  {
+    int id;                     /* Sleeper ID. */
+    int iterations;             /* Iterations so far. */
+    struct lock *lock;          /* Lock on output. */
+    int **op;                   /* Output buffer position. */
+  };
+
+#define THREAD_CNT 16
+#define ITER_CNT 16
+
+static thread_func simple_thread_func;
+
+void
+test_priority_fifo (void) 
+{
+  struct simple_thread_data data[THREAD_CNT];
+  struct lock lock;
+  int *output, *op;
+  int i, cnt;
+
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  /* Make sure our priority is the default. */
+  ASSERT (thread_get_priority () == PRI_DEFAULT);
+
+  msg ("%d threads will iterate %d times in the same order each time.",
+       THREAD_CNT, ITER_CNT);
+  msg ("If the order varies then there is a bug.");
+
+  output = op = malloc (sizeof *output * THREAD_CNT * ITER_CNT * 2);
+  ASSERT (output != NULL);
+  lock_init (&lock);
+
+  thread_set_priority (PRI_DEFAULT + 2);
+  for (i = 0; i < THREAD_CNT; i++) 
+    {
+      char name[16];
+      struct simple_thread_data *d = data + i;
+      snprintf (name, sizeof name, "%d", i);
+      d->id = i;
+      d->iterations = 0;
+      d->lock = &lock;
+      d->op = &op;
+      thread_create (name, PRI_DEFAULT + 1, simple_thread_func, d);
+    }
+
+  thread_set_priority (PRI_DEFAULT);
+  /* All the other threads now run to termination here. */
+  ASSERT (lock.holder == NULL);
+
+  cnt = 0;
+  for (; output < op; output++) 
+    {
+      struct simple_thread_data *d;
+
+      ASSERT (*output >= 0 && *output < THREAD_CNT);
+      d = data + *output;
+      if (cnt % THREAD_CNT == 0)
+        printf ("(priority-fifo) iteration:");
+      printf (" %d", d->id);
+      if (++cnt % THREAD_CNT == 0)
+        printf ("\n");
+      d->iterations++;
+    }
+}
+
+static void 
+simple_thread_func (void *data_) 
+{
+  struct simple_thread_data *data = data_;
+  int i;
+  
+  for (i = 0; i < ITER_CNT; i++) 
+    {
+      lock_acquire (data->lock);
+      *(*data->op)++ = data->id;
+      lock_release (data->lock);
+      thread_yield ();
+    }
+}
diff --git a/src/tests/threads/priority-fifo.ck b/src/tests/threads/priority-fifo.ck
new file mode 100644
index 0000000..11f1dd3
--- /dev/null
+++ b/src/tests/threads/priority-fifo.ck
@@ -0,0 +1,63 @@
+# -*- perl -*-
+
+# The expected output looks like this:
+#
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+# (priority-fifo) iteration: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
+#
+# A different permutation of 0...15 is acceptable, but every line must
+# be in the same order.
+
+use strict;
+use warnings;
+use tests::tests;
+
+our ($test);
+my (@output) = read_text_file ("$test.output");
+
+common_checks ("run", @output);
+
+my ($thread_cnt) = 16;
+my ($iter_cnt) = 16;
+my (@order);
+my (@t) = (-1) x $thread_cnt;
+
+my (@iterations) = grep (/iteration:/, @output);
+fail "No iterations found in output.\n" if !@iterations;
+
+my (@numbering) = $iterations[0] =~ /(\d+)/g;
+fail "First iteration does not list exactly $thread_cnt threads.\n"
+  if @numbering != $thread_cnt;
+
+my (@sorted_numbering) = sort { $a <=> $b } @numbering;
+for my $i (0...$#sorted_numbering) {
+    if ($sorted_numbering[$i] != $i) {
+	fail "First iteration does not list all threads "
+	  . "0...$#sorted_numbering\n";
+    }
+}
+
+for my $i (1...$#iterations) {
+    if ($iterations[$i] ne $iterations[0]) {
+	fail "Iteration $i differs from iteration 0\n";
+    }
+}
+
+fail "$iter_cnt iterations expected but " . scalar (@iterations)  . " found\n"
+  if $iter_cnt != @iterations;
+
+pass;
diff --git a/src/tests/threads/priority-preempt.c b/src/tests/threads/priority-preempt.c
new file mode 100644
index 0000000..3c3aacb
--- /dev/null
+++ b/src/tests/threads/priority-preempt.c
@@ -0,0 +1,41 @@
+/* Ensures that a high-priority thread really preempts.
+
+   Based on a test originally submitted for Stanford's CS 140 in
+   winter 1999 by by Matt Franklin
+   <startled@leland.stanford.edu>, Greg Hutchins
+   <gmh@leland.stanford.edu>, Yu Ping Hu <yph@cs.stanford.edu>.
+   Modified by arens. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+
+static thread_func simple_thread_func;
+
+void
+test_priority_preempt (void) 
+{
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  /* Make sure our priority is the default. */
+  ASSERT (thread_get_priority () == PRI_DEFAULT);
+
+  thread_create ("high-priority", PRI_DEFAULT + 1, simple_thread_func, NULL);
+  msg ("The high-priority thread should have already completed.");
+}
+
+static void 
+simple_thread_func (void *aux UNUSED) 
+{
+  int i;
+  
+  for (i = 0; i < 5; i++) 
+    {
+      msg ("Thread %s iteration %d", thread_name (), i);
+      thread_yield ();
+    }
+  msg ("Thread %s done!", thread_name ());
+}
diff --git a/src/tests/threads/priority-preempt.ck b/src/tests/threads/priority-preempt.ck
new file mode 100644
index 0000000..43a26ee
--- /dev/null
+++ b/src/tests/threads/priority-preempt.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-preempt) begin
+(priority-preempt) Thread high-priority iteration 0
+(priority-preempt) Thread high-priority iteration 1
+(priority-preempt) Thread high-priority iteration 2
+(priority-preempt) Thread high-priority iteration 3
+(priority-preempt) Thread high-priority iteration 4
+(priority-preempt) Thread high-priority done!
+(priority-preempt) The high-priority thread should have already completed.
+(priority-preempt) end
+EOF
+pass;
diff --git a/src/tests/threads/priority-sema.c b/src/tests/threads/priority-sema.c
new file mode 100644
index 0000000..2834a88
--- /dev/null
+++ b/src/tests/threads/priority-sema.c
@@ -0,0 +1,45 @@
+/* Tests that the highest-priority thread waiting on a semaphore
+   is the first to wake up. */
+
+#include <stdio.h>
+#include "tests/threads/tests.h"
+#include "threads/init.h"
+#include "threads/malloc.h"
+#include "threads/synch.h"
+#include "threads/thread.h"
+#include "devices/timer.h"
+
+static thread_func priority_sema_thread;
+static struct semaphore sema;
+
+void
+test_priority_sema (void) 
+{
+  int i;
+  
+  /* This test does not work with the MLFQS. */
+  ASSERT (!thread_mlfqs);
+
+  sema_init (&sema, 0);
+  thread_set_priority (PRI_MIN);
+  for (i = 0; i < 10; i++) 
+    {
+      int priority = PRI_DEFAULT - (i + 3) % 10 - 1;
+      char name[16];
+      snprintf (name, sizeof name, "priority %d", priority);
+      thread_create (name, priority, priority_sema_thread, NULL);
+    }
+
+  for (i = 0; i < 10; i++) 
+    {
+      sema_up (&sema);
+      msg ("Back in main thread."); 
+    }
+}
+
+static void
+priority_sema_thread (void *aux UNUSED) 
+{
+  sema_down (&sema);
+  msg ("Thread %s woke up.", thread_name ());
+}
diff --git a/src/tests/threads/priority-sema.ck b/src/tests/threads/priority-sema.ck
new file mode 100644
index 0000000..559988d
--- /dev/null
+++ b/src/tests/threads/priority-sema.ck
@@ -0,0 +1,29 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(priority-sema) begin
+(priority-sema) Thread priority 30 woke up.
+(priority-sema) Back in main thread.
+(priority-sema) Thread priority 29 woke up.
+(priority-sema) Back in main thread.
+(priority-sema) Thread priority 28 woke up.
+(priority-sema) Back in main thread.
+(priority-sema) Thread priority 27 woke up.
+(priority-sema) Back in main thread.
+(priority-sema) Thread priority 26 woke up.
+(priority-sema) Back in main thread.
+(priority-sema) Thread priority 25 woke up.
+(priority-sema) Back in main thread.
+(priority-sema) Thread priority 24 woke up.
+(priority-sema) Back in main thread.
+(priority-sema) Thread priority 23 woke up.
+(priority-sema) Back in main thread.
+(priority-sema) Thread priority 22 woke up.
+(priority-sema) Back in main thread.
+(priority-sema) Thread priority 21 woke up.
+(priority-sema) Back in main thread.
+(priority-sema) end
+EOF
+pass;
diff --git a/src/tests/threads/tests.c b/src/tests/threads/tests.c
new file mode 100644
index 0000000..af15aee
--- /dev/null
+++ b/src/tests/threads/tests.c
@@ -0,0 +1,102 @@
+#include "tests/threads/tests.h"
+#include <debug.h>
+#include <string.h>
+#include <stdio.h>
+
+struct test 
+  {
+    const char *name;
+    test_func *function;
+  };
+
+static const struct test tests[] = 
+  {
+    {"alarm-single", test_alarm_single},
+    {"alarm-multiple", test_alarm_multiple},
+    {"alarm-simultaneous", test_alarm_simultaneous},
+    {"alarm-priority", test_alarm_priority},
+    {"alarm-zero", test_alarm_zero},
+    {"alarm-negative", test_alarm_negative},
+    {"priority-change", test_priority_change},
+    {"priority-donate-one", test_priority_donate_one},
+    {"priority-donate-multiple", test_priority_donate_multiple},
+    {"priority-donate-multiple2", test_priority_donate_multiple2},
+    {"priority-donate-nest", test_priority_donate_nest},
+    {"priority-donate-sema", test_priority_donate_sema},
+    {"priority-donate-lower", test_priority_donate_lower},
+    {"priority-donate-chain", test_priority_donate_chain},
+    {"priority-fifo", test_priority_fifo},
+    {"priority-preempt", test_priority_preempt},
+    {"priority-sema", test_priority_sema},
+    {"priority-condvar", test_priority_condvar},
+    {"mlfqs-load-1", test_mlfqs_load_1},
+    {"mlfqs-load-60", test_mlfqs_load_60},
+    {"mlfqs-load-avg", test_mlfqs_load_avg},
+    {"mlfqs-recent-1", test_mlfqs_recent_1},
+    {"mlfqs-fair-2", test_mlfqs_fair_2},
+    {"mlfqs-fair-20", test_mlfqs_fair_20},
+    {"mlfqs-nice-2", test_mlfqs_nice_2},
+    {"mlfqs-nice-10", test_mlfqs_nice_10},
+    {"mlfqs-block", test_mlfqs_block},
+  };
+
+static const char *test_name;
+
+/* Runs the test named NAME. */
+void
+run_test (const char *name) 
+{
+  const struct test *t;
+
+  for (t = tests; t < tests + sizeof tests / sizeof *tests; t++)
+    if (!strcmp (name, t->name))
+      {
+        test_name = name;
+        msg ("begin");
+        t->function ();
+        msg ("end");
+        return;
+      }
+  PANIC ("no test named \"%s\"", name);
+}
+
+/* Prints FORMAT as if with printf(),
+   prefixing the output by the name of the test
+   and following it with a new-line character. */
+void
+msg (const char *format, ...) 
+{
+  va_list args;
+  
+  printf ("(%s) ", test_name);
+  va_start (args, format);
+  vprintf (format, args);
+  va_end (args);
+  putchar ('\n');
+}
+
+/* Prints failure message FORMAT as if with printf(),
+   prefixing the output by the name of the test and FAIL:
+   and following it with a new-line character,
+   and then panics the kernel. */
+void
+fail (const char *format, ...) 
+{
+  va_list args;
+  
+  printf ("(%s) FAIL: ", test_name);
+  va_start (args, format);
+  vprintf (format, args);
+  va_end (args);
+  putchar ('\n');
+
+  PANIC ("test failed");
+}
+
+/* Prints a message indicating the current test passed. */
+void
+pass (void) 
+{
+  printf ("(%s) PASS\n", test_name);
+}
+
diff --git a/src/tests/threads/tests.h b/src/tests/threads/tests.h
new file mode 100644
index 0000000..cd9d489
--- /dev/null
+++ b/src/tests/threads/tests.h
@@ -0,0 +1,41 @@
+#ifndef TESTS_THREADS_TESTS_H
+#define TESTS_THREADS_TESTS_H
+
+void run_test (const char *);
+
+typedef void test_func (void);
+
+extern test_func test_alarm_single;
+extern test_func test_alarm_multiple;
+extern test_func test_alarm_simultaneous;
+extern test_func test_alarm_priority;
+extern test_func test_alarm_zero;
+extern test_func test_alarm_negative;
+extern test_func test_priority_change;
+extern test_func test_priority_donate_one;
+extern test_func test_priority_donate_multiple;
+extern test_func test_priority_donate_multiple2;
+extern test_func test_priority_donate_sema;
+extern test_func test_priority_donate_nest;
+extern test_func test_priority_donate_lower;
+extern test_func test_priority_donate_chain;
+extern test_func test_priority_fifo;
+extern test_func test_priority_preempt;
+extern test_func test_priority_sema;
+extern test_func test_priority_condvar;
+extern test_func test_mlfqs_load_1;
+extern test_func test_mlfqs_load_60;
+extern test_func test_mlfqs_load_avg;
+extern test_func test_mlfqs_recent_1;
+extern test_func test_mlfqs_fair_2;
+extern test_func test_mlfqs_fair_20;
+extern test_func test_mlfqs_nice_2;
+extern test_func test_mlfqs_nice_10;
+extern test_func test_mlfqs_block;
+
+void msg (const char *, ...);
+void fail (const char *, ...);
+void pass (void);
+
+#endif /* tests/threads/tests.h */
+
diff --git a/src/tests/userprog/Grading b/src/tests/userprog/Grading
new file mode 100644
index 0000000..f70dc99
--- /dev/null
+++ b/src/tests/userprog/Grading
@@ -0,0 +1,11 @@
+# Percentage of the testing point total designated for each set of
+# tests.
+
+# This project is primarily about implementing system calls.
+# If you do so properly, the base file system functionality
+# should come "for free".  Thus, the points emphasis below.
+
+35%	tests/userprog/Rubric.functionality
+25%	tests/userprog/Rubric.robustness
+10%	tests/userprog/no-vm/Rubric
+30%	tests/filesys/base/Rubric
diff --git a/src/tests/userprog/Make.tests b/src/tests/userprog/Make.tests
new file mode 100644
index 0000000..a60b1f0
--- /dev/null
+++ b/src/tests/userprog/Make.tests
@@ -0,0 +1,143 @@
+# -*- makefile -*-
+
+tests/%.output: FILESYSSOURCE = --filesys-size=2
+tests/%.output: PUTFILES = $(filter-out kernel.bin loader.bin, $^)
+
+tests/userprog_TESTS = $(addprefix tests/userprog/,args-none            \
+args-single args-multiple args-many args-dbl-space sc-bad-sp            \
+sc-bad-arg sc-boundary sc-boundary-2 sc-boundary-3 halt exit            \
+create-normal create-empty create-null create-bad-ptr create-long       \
+create-exists create-bound open-normal open-missing open-boundary       \
+open-empty open-null open-bad-ptr open-twice close-normal               \
+close-twice close-stdin close-stdout close-bad-fd read-normal           \
+read-bad-ptr read-boundary read-zero read-stdout read-bad-fd            \
+write-normal write-bad-ptr write-boundary write-zero write-stdin        \
+write-bad-fd exec-once exec-arg exec-bound exec-bound-2                 \
+exec-bound-3 exec-multiple exec-missing exec-bad-ptr wait-simple        \
+wait-twice wait-killed wait-bad-pid multi-recurse multi-child-fd        \
+rox-simple rox-child rox-multichild bad-read bad-write bad-read2        \
+bad-write2 bad-jump bad-jump2)
+
+tests/userprog_PROGS = $(tests/userprog_TESTS) $(addprefix \
+tests/userprog/,child-simple child-args child-bad child-close child-rox)
+
+tests/userprog/args-none_SRC = tests/userprog/args.c
+tests/userprog/args-single_SRC = tests/userprog/args.c
+tests/userprog/args-multiple_SRC = tests/userprog/args.c
+tests/userprog/args-many_SRC = tests/userprog/args.c
+tests/userprog/args-dbl-space_SRC = tests/userprog/args.c
+tests/userprog/sc-bad-sp_SRC = tests/userprog/sc-bad-sp.c tests/main.c
+tests/userprog/sc-bad-arg_SRC = tests/userprog/sc-bad-arg.c tests/main.c
+tests/userprog/bad-read_SRC = tests/userprog/bad-read.c tests/main.c
+tests/userprog/bad-write_SRC = tests/userprog/bad-write.c tests/main.c
+tests/userprog/bad-jump_SRC = tests/userprog/bad-jump.c tests/main.c
+tests/userprog/bad-read2_SRC = tests/userprog/bad-read2.c tests/main.c
+tests/userprog/bad-write2_SRC = tests/userprog/bad-write2.c tests/main.c
+tests/userprog/bad-jump2_SRC = tests/userprog/bad-jump2.c tests/main.c
+tests/userprog/sc-boundary_SRC = tests/userprog/sc-boundary.c           \
+tests/userprog/boundary.c tests/main.c
+tests/userprog/sc-boundary-2_SRC = tests/userprog/sc-boundary-2.c	\
+tests/userprog/boundary.c tests/main.c
+tests/userprog/sc-boundary-3_SRC = tests/userprog/sc-boundary-3.c	\
+tests/userprog/boundary.c tests/main.c
+tests/userprog/halt_SRC = tests/userprog/halt.c tests/main.c
+tests/userprog/exit_SRC = tests/userprog/exit.c tests/main.c
+tests/userprog/create-normal_SRC = tests/userprog/create-normal.c tests/main.c
+tests/userprog/create-empty_SRC = tests/userprog/create-empty.c tests/main.c
+tests/userprog/create-null_SRC = tests/userprog/create-null.c tests/main.c
+tests/userprog/create-bad-ptr_SRC = tests/userprog/create-bad-ptr.c	\
+tests/main.c
+tests/userprog/create-long_SRC = tests/userprog/create-long.c tests/main.c
+tests/userprog/create-exists_SRC = tests/userprog/create-exists.c tests/main.c
+tests/userprog/create-bound_SRC = tests/userprog/create-bound.c	\
+tests/userprog/boundary.c tests/main.c
+tests/userprog/open-normal_SRC = tests/userprog/open-normal.c tests/main.c
+tests/userprog/open-missing_SRC = tests/userprog/open-missing.c tests/main.c
+tests/userprog/open-boundary_SRC = tests/userprog/open-boundary.c	\
+tests/userprog/boundary.c tests/main.c
+tests/userprog/open-empty_SRC = tests/userprog/open-empty.c tests/main.c
+tests/userprog/open-null_SRC = tests/userprog/open-null.c tests/main.c
+tests/userprog/open-bad-ptr_SRC = tests/userprog/open-bad-ptr.c tests/main.c
+tests/userprog/open-twice_SRC = tests/userprog/open-twice.c tests/main.c
+tests/userprog/close-normal_SRC = tests/userprog/close-normal.c tests/main.c
+tests/userprog/close-twice_SRC = tests/userprog/close-twice.c tests/main.c
+tests/userprog/close-stdin_SRC = tests/userprog/close-stdin.c tests/main.c
+tests/userprog/close-stdout_SRC = tests/userprog/close-stdout.c tests/main.c
+tests/userprog/close-bad-fd_SRC = tests/userprog/close-bad-fd.c tests/main.c
+tests/userprog/read-normal_SRC = tests/userprog/read-normal.c tests/main.c
+tests/userprog/read-bad-ptr_SRC = tests/userprog/read-bad-ptr.c tests/main.c
+tests/userprog/read-boundary_SRC = tests/userprog/read-boundary.c	\
+tests/userprog/boundary.c tests/main.c
+tests/userprog/read-zero_SRC = tests/userprog/read-zero.c tests/main.c
+tests/userprog/read-stdout_SRC = tests/userprog/read-stdout.c tests/main.c
+tests/userprog/read-bad-fd_SRC = tests/userprog/read-bad-fd.c tests/main.c
+tests/userprog/write-normal_SRC = tests/userprog/write-normal.c tests/main.c
+tests/userprog/write-bad-ptr_SRC = tests/userprog/write-bad-ptr.c tests/main.c
+tests/userprog/write-boundary_SRC = tests/userprog/write-boundary.c	\
+tests/userprog/boundary.c tests/main.c
+tests/userprog/write-zero_SRC = tests/userprog/write-zero.c tests/main.c
+tests/userprog/write-stdin_SRC = tests/userprog/write-stdin.c tests/main.c
+tests/userprog/write-bad-fd_SRC = tests/userprog/write-bad-fd.c tests/main.c
+tests/userprog/exec-once_SRC = tests/userprog/exec-once.c tests/main.c
+tests/userprog/exec-arg_SRC = tests/userprog/exec-arg.c tests/main.c
+tests/userprog/exec-bound_SRC = tests/userprog/exec-bound.c       \
+tests/userprog/boundary.c  tests/main.c
+tests/userprog/exec-bound-2_SRC = tests/userprog/exec-bound-2.c         \
+tests/userprog/boundary.c  tests/main.c
+tests/userprog/exec-bound-3_SRC = tests/userprog/exec-bound-3.c         \
+tests/userprog/boundary.c  tests/main.c
+tests/userprog/exec-multiple_SRC = tests/userprog/exec-multiple.c tests/main.c
+tests/userprog/exec-missing_SRC = tests/userprog/exec-missing.c tests/main.c
+tests/userprog/exec-bad-ptr_SRC = tests/userprog/exec-bad-ptr.c tests/main.c
+tests/userprog/wait-simple_SRC = tests/userprog/wait-simple.c tests/main.c
+tests/userprog/wait-twice_SRC = tests/userprog/wait-twice.c tests/main.c
+tests/userprog/wait-killed_SRC = tests/userprog/wait-killed.c tests/main.c
+tests/userprog/wait-bad-pid_SRC = tests/userprog/wait-bad-pid.c tests/main.c
+tests/userprog/multi-recurse_SRC = tests/userprog/multi-recurse.c
+tests/userprog/multi-child-fd_SRC = tests/userprog/multi-child-fd.c	\
+tests/main.c
+tests/userprog/rox-simple_SRC = tests/userprog/rox-simple.c tests/main.c
+tests/userprog/rox-child_SRC = tests/userprog/rox-child.c tests/main.c
+tests/userprog/rox-multichild_SRC = tests/userprog/rox-multichild.c	\
+tests/main.c
+
+tests/userprog/child-simple_SRC = tests/userprog/child-simple.c
+tests/userprog/child-args_SRC = tests/userprog/args.c
+tests/userprog/child-bad_SRC = tests/userprog/child-bad.c tests/main.c
+tests/userprog/child-close_SRC = tests/userprog/child-close.c
+tests/userprog/child-rox_SRC = tests/userprog/child-rox.c
+
+$(foreach prog,$(tests/userprog_PROGS),$(eval $(prog)_SRC += tests/lib.c))
+
+tests/userprog/args-single_ARGS = onearg
+tests/userprog/args-multiple_ARGS = some arguments for you!
+tests/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
+tests/userprog/args-dbl-space_ARGS = two  spaces!
+tests/userprog/multi-recurse_ARGS = 15
+
+tests/userprog/open-normal_PUTFILES += tests/userprog/sample.txt
+tests/userprog/open-boundary_PUTFILES += tests/userprog/sample.txt
+tests/userprog/open-twice_PUTFILES += tests/userprog/sample.txt
+tests/userprog/close-normal_PUTFILES += tests/userprog/sample.txt
+tests/userprog/close-twice_PUTFILES += tests/userprog/sample.txt
+tests/userprog/read-normal_PUTFILES += tests/userprog/sample.txt
+tests/userprog/read-bad-ptr_PUTFILES += tests/userprog/sample.txt
+tests/userprog/read-boundary_PUTFILES += tests/userprog/sample.txt
+tests/userprog/read-zero_PUTFILES += tests/userprog/sample.txt
+tests/userprog/write-normal_PUTFILES += tests/userprog/sample.txt
+tests/userprog/write-bad-ptr_PUTFILES += tests/userprog/sample.txt
+tests/userprog/write-boundary_PUTFILES += tests/userprog/sample.txt
+tests/userprog/write-zero_PUTFILES += tests/userprog/sample.txt
+tests/userprog/multi-child-fd_PUTFILES += tests/userprog/sample.txt
+
+tests/userprog/exec-once_PUTFILES += tests/userprog/child-simple
+tests/userprog/exec-multiple_PUTFILES += tests/userprog/child-simple
+tests/userprog/wait-simple_PUTFILES += tests/userprog/child-simple
+tests/userprog/wait-twice_PUTFILES += tests/userprog/child-simple
+
+tests/userprog/exec-arg_PUTFILES += tests/userprog/child-args
+tests/userprog/exec-bound_PUTFILES += tests/userprog/child-args
+tests/userprog/multi-child-fd_PUTFILES += tests/userprog/child-close
+tests/userprog/wait-killed_PUTFILES += tests/userprog/child-bad
+tests/userprog/rox-child_PUTFILES += tests/userprog/child-rox
+tests/userprog/rox-multichild_PUTFILES += tests/userprog/child-rox
diff --git a/src/tests/userprog/Rubric.functionality b/src/tests/userprog/Rubric.functionality
new file mode 100644
index 0000000..ea76c44
--- /dev/null
+++ b/src/tests/userprog/Rubric.functionality
@@ -0,0 +1,52 @@
+Functionality of system calls:
+- Test argument passing on Pintos command line.
+3	args-none
+3	args-single
+3	args-multiple
+3	args-many
+3	args-dbl-space
+
+- Test "create" system call.
+3	create-empty
+3	create-long
+3	create-normal
+3	create-exists
+
+- Test "open" system call.
+3	open-missing
+3	open-normal
+3	open-twice
+
+- Test "read" system call.
+3	read-normal
+3	read-zero
+
+- Test "write" system call.
+3	write-normal
+3	write-zero
+
+- Test "close" system call.
+3	close-normal
+
+- Test "exec" system call.
+5	exec-once
+5	exec-multiple
+5	exec-arg
+
+- Test "wait" system call.
+5	wait-simple
+5	wait-twice
+
+- Test "exit" system call.
+5	exit
+
+- Test "halt" system call.
+3	halt
+
+- Test recursive execution of user programs.
+15	multi-recurse
+
+- Test read-only executable feature.
+3	rox-simple
+3	rox-child
+3	rox-multichild
diff --git a/src/tests/userprog/Rubric.robustness b/src/tests/userprog/Rubric.robustness
new file mode 100644
index 0000000..b7d1035
--- /dev/null
+++ b/src/tests/userprog/Rubric.robustness
@@ -0,0 +1,48 @@
+Robustness of system calls:
+- Test robustness of file descriptor handling.
+2	close-stdin
+2	close-stdout
+2	close-bad-fd
+2	close-twice
+2	read-bad-fd
+2	read-stdout
+2	write-bad-fd
+2	write-stdin
+2	multi-child-fd
+
+- Test robustness of pointer handling.
+3	create-bad-ptr
+3	exec-bad-ptr
+3	open-bad-ptr
+3	read-bad-ptr
+3	write-bad-ptr
+
+- Test robustness of buffer copying across page boundaries.
+3	create-bound
+3	open-boundary
+3	read-boundary
+3	write-boundary
+
+- Test handling of null pointer and empty strings.
+2	create-null
+2	open-null
+2	open-empty
+
+- Test robustness of system call implementation.
+3	sc-bad-arg
+3	sc-bad-sp
+5	sc-boundary
+5	sc-boundary-2
+
+- Test robustness of "exec" and "wait" system calls.
+5	exec-missing
+5	wait-bad-pid
+5	wait-killed
+
+- Test robustness of exception handling.
+1	bad-read
+1	bad-write
+1	bad-jump
+1	bad-read2
+1	bad-write2
+1	bad-jump2
diff --git a/src/tests/userprog/args-dbl-space.ck b/src/tests/userprog/args-dbl-space.ck
new file mode 100644
index 0000000..dfbcf4b
--- /dev/null
+++ b/src/tests/userprog/args-dbl-space.ck
@@ -0,0 +1,15 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(args) begin
+(args) argc = 3
+(args) argv[0] = 'args-dbl-space'
+(args) argv[1] = 'two'
+(args) argv[2] = 'spaces!'
+(args) argv[3] = null
+(args) end
+args-dbl-space: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/args-many.ck b/src/tests/userprog/args-many.ck
new file mode 100644
index 0000000..214574a
--- /dev/null
+++ b/src/tests/userprog/args-many.ck
@@ -0,0 +1,35 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(args) begin
+(args) argc = 23
+(args) argv[0] = 'args-many'
+(args) argv[1] = 'a'
+(args) argv[2] = 'b'
+(args) argv[3] = 'c'
+(args) argv[4] = 'd'
+(args) argv[5] = 'e'
+(args) argv[6] = 'f'
+(args) argv[7] = 'g'
+(args) argv[8] = 'h'
+(args) argv[9] = 'i'
+(args) argv[10] = 'j'
+(args) argv[11] = 'k'
+(args) argv[12] = 'l'
+(args) argv[13] = 'm'
+(args) argv[14] = 'n'
+(args) argv[15] = 'o'
+(args) argv[16] = 'p'
+(args) argv[17] = 'q'
+(args) argv[18] = 'r'
+(args) argv[19] = 's'
+(args) argv[20] = 't'
+(args) argv[21] = 'u'
+(args) argv[22] = 'v'
+(args) argv[23] = null
+(args) end
+args-many: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/args-multiple.ck b/src/tests/userprog/args-multiple.ck
new file mode 100644
index 0000000..227e6cc
--- /dev/null
+++ b/src/tests/userprog/args-multiple.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(args) begin
+(args) argc = 5
+(args) argv[0] = 'args-multiple'
+(args) argv[1] = 'some'
+(args) argv[2] = 'arguments'
+(args) argv[3] = 'for'
+(args) argv[4] = 'you!'
+(args) argv[5] = null
+(args) end
+args-multiple: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/args-none.ck b/src/tests/userprog/args-none.ck
new file mode 100644
index 0000000..146318e
--- /dev/null
+++ b/src/tests/userprog/args-none.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(args) begin
+(args) argc = 1
+(args) argv[0] = 'args-none'
+(args) argv[1] = null
+(args) end
+args-none: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/args-single.ck b/src/tests/userprog/args-single.ck
new file mode 100644
index 0000000..24582b4
--- /dev/null
+++ b/src/tests/userprog/args-single.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(args) begin
+(args) argc = 2
+(args) argv[0] = 'args-single'
+(args) argv[1] = 'onearg'
+(args) argv[2] = null
+(args) end
+args-single: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/args.c b/src/tests/userprog/args.c
new file mode 100644
index 0000000..20eda44
--- /dev/null
+++ b/src/tests/userprog/args.c
@@ -0,0 +1,25 @@
+/* Prints the command-line arguments.
+   This program is used for all of the args-* tests.  Grading is
+   done differently for each of the args-* tests based on the
+   output. */
+
+#include "tests/lib.h"
+
+int
+main (int argc, char *argv[]) 
+{
+  int i;
+
+  test_name = "args";
+
+  msg ("begin");
+  msg ("argc = %d", argc);
+  for (i = 0; i <= argc; i++)
+    if (argv[i] != NULL)
+      msg ("argv[%d] = '%s'", i, argv[i]);
+    else
+      msg ("argv[%d] = null", i);
+  msg ("end");
+
+  return 0;
+}
diff --git a/src/tests/userprog/bad-jump.c b/src/tests/userprog/bad-jump.c
new file mode 100644
index 0000000..a915c39
--- /dev/null
+++ b/src/tests/userprog/bad-jump.c
@@ -0,0 +1,16 @@
+/* This program attempts to execute code at address 0, which is not mapped.
+   This should terminate the process with a -1 exit code. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+typedef int (* volatile functionptr)(void);
+
+void
+test_main (void) 
+{
+  functionptr fp = NULL;
+  msg ("Congratulations - you have successfully called NULL: %d", 
+        fp());
+  fail ("should have exited with -1");
+}
diff --git a/src/tests/userprog/bad-jump.ck b/src/tests/userprog/bad-jump.ck
new file mode 100644
index 0000000..e1c178b
--- /dev/null
+++ b/src/tests/userprog/bad-jump.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
+(bad-jump) begin
+bad-jump: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/bad-jump2.c b/src/tests/userprog/bad-jump2.c
new file mode 100644
index 0000000..dc7c2a7
--- /dev/null
+++ b/src/tests/userprog/bad-jump2.c
@@ -0,0 +1,13 @@
+/* This program attempts to execute code at a kernel virtual address. 
+   This should terminate the process with a -1 exit code. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("Congratulations - you have successfully called kernel code: %d", 
+        ((int (*)(void))0xC0000000)());
+  fail ("should have exited with -1");
+}
diff --git a/src/tests/userprog/bad-jump2.ck b/src/tests/userprog/bad-jump2.ck
new file mode 100644
index 0000000..35f0f97
--- /dev/null
+++ b/src/tests/userprog/bad-jump2.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
+(bad-jump2) begin
+bad-jump2: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/bad-read.c b/src/tests/userprog/bad-read.c
new file mode 100644
index 0000000..d821a9a
--- /dev/null
+++ b/src/tests/userprog/bad-read.c
@@ -0,0 +1,13 @@
+/* This program attempts to read memory at an address that is not mapped.
+   This should terminate the process with a -1 exit code. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("Congratulations - you have successfully dereferenced NULL: %d", 
+        *(volatile int *) NULL);
+  fail ("should have exited with -1");
+}
diff --git a/src/tests/userprog/bad-read.ck b/src/tests/userprog/bad-read.ck
new file mode 100644
index 0000000..4d4d926
--- /dev/null
+++ b/src/tests/userprog/bad-read.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
+(bad-read) begin
+bad-read: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/bad-read2.c b/src/tests/userprog/bad-read2.c
new file mode 100644
index 0000000..a2fc237
--- /dev/null
+++ b/src/tests/userprog/bad-read2.c
@@ -0,0 +1,13 @@
+/* This program attempts to read kernel memory. 
+   This should terminate the process with a -1 exit code. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("Congratulations - you have successfully read kernel memory: %d", 
+        *(int *)0xC0000000);
+  fail ("should have exited with -1");
+}
diff --git a/src/tests/userprog/bad-read2.ck b/src/tests/userprog/bad-read2.ck
new file mode 100644
index 0000000..fa27c7d
--- /dev/null
+++ b/src/tests/userprog/bad-read2.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
+(bad-read2) begin
+bad-read2: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/bad-write.c b/src/tests/userprog/bad-write.c
new file mode 100644
index 0000000..9082c9b
--- /dev/null
+++ b/src/tests/userprog/bad-write.c
@@ -0,0 +1,12 @@
+/* This program attempts to write to memory at an address that is not mapped.
+   This should terminate the process with a -1 exit code. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  *(volatile int *)NULL = 42;
+  fail ("should have exited with -1");
+}
diff --git a/src/tests/userprog/bad-write.ck b/src/tests/userprog/bad-write.ck
new file mode 100644
index 0000000..d213b49
--- /dev/null
+++ b/src/tests/userprog/bad-write.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
+(bad-write) begin
+bad-write: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/bad-write2.c b/src/tests/userprog/bad-write2.c
new file mode 100644
index 0000000..753da1e
--- /dev/null
+++ b/src/tests/userprog/bad-write2.c
@@ -0,0 +1,12 @@
+/* This program attempts to write to kernel memory. 
+   This should terminate the process with a -1 exit code. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  *(int *)0xC0000000 = 42;
+  fail ("should have exited with -1");
+}
diff --git a/src/tests/userprog/bad-write2.ck b/src/tests/userprog/bad-write2.ck
new file mode 100644
index 0000000..c6a3420
--- /dev/null
+++ b/src/tests/userprog/bad-write2.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
+(bad-write2) begin
+bad-write2: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/boundary.c b/src/tests/userprog/boundary.c
new file mode 100644
index 0000000..ca4fb73
--- /dev/null
+++ b/src/tests/userprog/boundary.c
@@ -0,0 +1,47 @@
+/* Utility function for tests that try to break system calls by
+   passing them data that crosses from one virtual page to
+   another. */
+
+#include <inttypes.h>
+#include <round.h>
+#include <string.h>
+#include "tests/userprog/boundary.h"
+
+/* Together with statements in src/lib/user/user.lds, arranges
+   for the following array to be at the very end of the .bss
+   segment (needed for get_bad_boundary below). */
+static char dst[8192] __attribute__ ((section (".testEndmem,\"aw\",@nobits#")));
+
+/* Returns the beginning of a page.  There are at least 2048
+   modifiable bytes on either side of the pointer returned. */
+void *
+get_boundary_area (void) 
+{
+  char *p = (char *) ROUND_UP ((uintptr_t) dst, 4096);
+  if (p - dst < 2048)
+    p += 4096;
+  return p;
+}
+
+/* Returns a copy of SRC split across the boundary between two
+   pages. */
+char *
+copy_string_across_boundary (const char *src) 
+{
+  char *p = get_boundary_area ();
+  p -= strlen (src) < 4096 ? strlen (src) / 2 : 4096;
+  strlcpy (p, src, 4096);
+  return p;
+}
+
+/* Returns an address that is invalid, but the preceding bytes
+ * are all valid (the highest address in the bss segment). Used
+ * to position information such that the first byte of the
+ * information is valid, but not all the information is valid. */
+void *
+get_bad_boundary (void)
+{
+  /* This code assumes that dst will be in the highest page
+   * allocated to the user process. */
+  return (void *) ROUND_UP ((uintptr_t) (dst + sizeof(dst) - 1), 4096);
+}
diff --git a/src/tests/userprog/boundary.h b/src/tests/userprog/boundary.h
new file mode 100644
index 0000000..9fadb50
--- /dev/null
+++ b/src/tests/userprog/boundary.h
@@ -0,0 +1,8 @@
+#ifndef TESTS_USERPROG_BOUNDARY_H
+#define TESTS_USERPROG_BOUNDARY_H
+
+void *get_boundary_area (void);
+char *copy_string_across_boundary (const char *);
+void *get_bad_boundary (void);
+
+#endif /* tests/userprog/boundary.h */
diff --git a/src/tests/userprog/child-bad.c b/src/tests/userprog/child-bad.c
new file mode 100644
index 0000000..77d7a69
--- /dev/null
+++ b/src/tests/userprog/child-bad.c
@@ -0,0 +1,14 @@
+/* Child process run by wait-killed test.
+   Sets the stack pointer (%esp) to an invalid value and invokes
+   a system call, which should then terminate the process with a
+   -1 exit code. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  asm volatile ("movl $0x20101234, %esp; int $0x30");
+  fail ("should have exited with -1");
+}
diff --git a/src/tests/userprog/child-close.c b/src/tests/userprog/child-close.c
new file mode 100644
index 0000000..866e159
--- /dev/null
+++ b/src/tests/userprog/child-close.c
@@ -0,0 +1,28 @@
+/* Child process run by multi-child-fd test.
+
+   Attempts to close the file descriptor passed as the first
+   command-line argument.  This is invalid, because file
+   descriptors are not inherited in Pintos.  Two results are
+   allowed: either the system call should return without taking
+   any action, or the kernel should terminate the process with a
+   -1 exit code. */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include "tests/lib.h"
+
+int
+main (int argc UNUSED, char *argv[]) 
+{
+  test_name = "child-close";
+
+  msg ("begin");
+  if (!isdigit (*argv[1]))
+    fail ("bad command-line arguments");
+  close (atoi (argv[1]));
+  msg ("end");
+
+  return 0;
+}
diff --git a/src/tests/userprog/child-rox.c b/src/tests/userprog/child-rox.c
new file mode 100644
index 0000000..6fb2fc9
--- /dev/null
+++ b/src/tests/userprog/child-rox.c
@@ -0,0 +1,55 @@
+/* Child process run by rox-child and rox-multichild tests.
+   Opens and tries to write to its own executable, verifying that
+   that is disallowed.
+   Then recursively executes itself to the depth indicated by the
+   first command-line argument. */
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <syscall.h>
+#include "tests/lib.h"
+
+static void
+try_write (void) 
+{
+  int handle;
+  char buffer[19];
+
+  quiet = true;
+  CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\"");
+  quiet = false;
+
+  CHECK (write (handle, buffer, sizeof buffer) == 0,
+         "try to write \"child-rox\"");
+  
+  close (handle);
+}
+
+int
+main (int argc UNUSED, char *argv[]) 
+{
+  test_name = "child-rox";
+
+  msg ("begin");
+  try_write ();
+
+  if (!isdigit (*argv[1]))
+    fail ("bad command-line arguments");
+  if (atoi (argv[1]) > 1) 
+    {
+      char cmd[128];
+      int child;
+      
+      snprintf (cmd, sizeof cmd, "child-rox %d", atoi (argv[1]) - 1);
+      CHECK ((child = exec (cmd)) != -1, "exec \"%s\"", cmd);
+      quiet = true;
+      CHECK (wait (child) == 12, "wait for \"child-rox\"");
+      quiet = false;
+    }
+
+  try_write ();
+  msg ("end");
+
+  return 12;
+}
diff --git a/src/tests/userprog/child-simple.c b/src/tests/userprog/child-simple.c
new file mode 100644
index 0000000..06cce89
--- /dev/null
+++ b/src/tests/userprog/child-simple.c
@@ -0,0 +1,15 @@
+/* Child process run by exec-multiple, exec-one, wait-simple, and
+   wait-twice tests.
+   Just prints a single message and terminates. */
+
+#include <stdio.h>
+#include "tests/lib.h"
+
+int
+main (void) 
+{
+  test_name = "child-simple";
+
+  msg ("run");
+  return 81;
+}
diff --git a/src/tests/userprog/close-bad-fd.c b/src/tests/userprog/close-bad-fd.c
new file mode 100644
index 0000000..f63bb9a
--- /dev/null
+++ b/src/tests/userprog/close-bad-fd.c
@@ -0,0 +1,11 @@
+/* Tries to close an invalid fd, which must either fail silently
+   or terminate with exit code -1. */
+
+#include <syscall.h>
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  close (0x20101234);
+}
diff --git a/src/tests/userprog/close-bad-fd.ck b/src/tests/userprog/close-bad-fd.ck
new file mode 100644
index 0000000..497b17c
--- /dev/null
+++ b/src/tests/userprog/close-bad-fd.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(close-bad-fd) begin
+(close-bad-fd) end
+close-bad-fd: exit(0)
+EOF
+(close-bad-fd) begin
+close-bad-fd: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/close-normal.c b/src/tests/userprog/close-normal.c
new file mode 100644
index 0000000..8ce04e3
--- /dev/null
+++ b/src/tests/userprog/close-normal.c
@@ -0,0 +1,14 @@
+/* Opens a file and then closes it. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle;
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  msg ("close \"sample.txt\"");
+  close (handle);
+}
diff --git a/src/tests/userprog/close-normal.ck b/src/tests/userprog/close-normal.ck
new file mode 100644
index 0000000..fe41342
--- /dev/null
+++ b/src/tests/userprog/close-normal.ck
@@ -0,0 +1,12 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(close-normal) begin
+(close-normal) open "sample.txt"
+(close-normal) close "sample.txt"
+(close-normal) end
+close-normal: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/close-stdin.c b/src/tests/userprog/close-stdin.c
new file mode 100644
index 0000000..9bbf9f2
--- /dev/null
+++ b/src/tests/userprog/close-stdin.c
@@ -0,0 +1,11 @@
+/* Tries to close the keyboard input stream, which must either
+   fail silently or terminate with exit code -1. */
+
+#include <syscall.h>
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  close (0);
+}
diff --git a/src/tests/userprog/close-stdin.ck b/src/tests/userprog/close-stdin.ck
new file mode 100644
index 0000000..3d28507
--- /dev/null
+++ b/src/tests/userprog/close-stdin.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(close-stdin) begin
+(close-stdin) end
+close-stdin: exit(0)
+EOF
+(close-stdin) begin
+close-stdin: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/close-stdout.c b/src/tests/userprog/close-stdout.c
new file mode 100644
index 0000000..886523f
--- /dev/null
+++ b/src/tests/userprog/close-stdout.c
@@ -0,0 +1,11 @@
+/* Tries to close the console output stream, which must either
+   fail silently or terminate with exit code -1. */
+
+#include <syscall.h>
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  close (1);
+}
diff --git a/src/tests/userprog/close-stdout.ck b/src/tests/userprog/close-stdout.ck
new file mode 100644
index 0000000..3cbbcff
--- /dev/null
+++ b/src/tests/userprog/close-stdout.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(close-stdout) begin
+(close-stdout) end
+close-stdout: exit(0)
+EOF
+(close-stdout) begin
+close-stdout: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/close-twice.c b/src/tests/userprog/close-twice.c
new file mode 100644
index 0000000..830bccf
--- /dev/null
+++ b/src/tests/userprog/close-twice.c
@@ -0,0 +1,18 @@
+/* Opens a file and then tries to close it twice.  The second
+   close must either fail silently or terminate with exit code
+   -1. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle;
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  msg ("close \"sample.txt\"");
+  close (handle);
+  msg ("close \"sample.txt\" again");
+  close (handle);
+}
diff --git a/src/tests/userprog/close-twice.ck b/src/tests/userprog/close-twice.ck
new file mode 100644
index 0000000..deb55a6
--- /dev/null
+++ b/src/tests/userprog/close-twice.ck
@@ -0,0 +1,19 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(close-twice) begin
+(close-twice) open "sample.txt"
+(close-twice) close "sample.txt"
+(close-twice) close "sample.txt" again
+(close-twice) end
+close-twice: exit(0)
+EOF
+(close-twice) begin
+(close-twice) open "sample.txt"
+(close-twice) close "sample.txt"
+(close-twice) close "sample.txt" again
+close-twice: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/create-bad-ptr.c b/src/tests/userprog/create-bad-ptr.c
new file mode 100644
index 0000000..4a07bb3
--- /dev/null
+++ b/src/tests/userprog/create-bad-ptr.c
@@ -0,0 +1,12 @@
+/* Passes a bad pointer to the create system call,
+   which must cause the process to be terminated with exit code
+   -1. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("create(0x20101234): %d", create ((char *) 0x20101234, 0));
+}
diff --git a/src/tests/userprog/create-bad-ptr.ck b/src/tests/userprog/create-bad-ptr.ck
new file mode 100644
index 0000000..ac13405
--- /dev/null
+++ b/src/tests/userprog/create-bad-ptr.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(create-bad-ptr) begin
+create-bad-ptr: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/create-bound.c b/src/tests/userprog/create-bound.c
new file mode 100644
index 0000000..0a829f3
--- /dev/null
+++ b/src/tests/userprog/create-bound.c
@@ -0,0 +1,14 @@
+/* Opens a file whose name spans the boundary between two pages.
+   This is valid, so it must succeed. */
+
+#include <syscall.h>
+#include "tests/userprog/boundary.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("create(\"quux.dat\"): %d",
+       create (copy_string_across_boundary ("quux.dat"), 0));
+}
diff --git a/src/tests/userprog/create-bound.ck b/src/tests/userprog/create-bound.ck
new file mode 100644
index 0000000..7656b7f
--- /dev/null
+++ b/src/tests/userprog/create-bound.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(create-bound) begin
+(create-bound) create("quux.dat"): 1
+(create-bound) end
+create-bound: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/create-empty.c b/src/tests/userprog/create-empty.c
new file mode 100644
index 0000000..fa26b43
--- /dev/null
+++ b/src/tests/userprog/create-empty.c
@@ -0,0 +1,10 @@
+/* Tries to create a file with the empty string as its name. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("create(\"\"): %d", create ("", 0));
+}
diff --git a/src/tests/userprog/create-empty.ck b/src/tests/userprog/create-empty.ck
new file mode 100644
index 0000000..93a1058
--- /dev/null
+++ b/src/tests/userprog/create-empty.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(create-empty) begin
+(create-empty) create(""): 0
+(create-empty) end
+create-empty: exit(0)
+EOF
+(create-empty) begin
+create-empty: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/create-exists.c b/src/tests/userprog/create-exists.c
new file mode 100644
index 0000000..d395008
--- /dev/null
+++ b/src/tests/userprog/create-exists.c
@@ -0,0 +1,16 @@
+/* Verifies that trying to create a file under a name that
+   already exists will fail. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (create ("quux.dat", 0), "create quux.dat");
+  CHECK (create ("warble.dat", 0), "create warble.dat");
+  CHECK (!create ("quux.dat", 0), "try to re-create quux.dat");
+  CHECK (create ("baffle.dat", 0), "create baffle.dat");
+  CHECK (!create ("warble.dat", 0), "try to re-create quux.dat");
+}
diff --git a/src/tests/userprog/create-exists.ck b/src/tests/userprog/create-exists.ck
new file mode 100644
index 0000000..006885e
--- /dev/null
+++ b/src/tests/userprog/create-exists.ck
@@ -0,0 +1,15 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(create-exists) begin
+(create-exists) create quux.dat
+(create-exists) create warble.dat
+(create-exists) try to re-create quux.dat
+(create-exists) create baffle.dat
+(create-exists) try to re-create quux.dat
+(create-exists) end
+create-exists: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/create-long.c b/src/tests/userprog/create-long.c
new file mode 100644
index 0000000..16b31bd
--- /dev/null
+++ b/src/tests/userprog/create-long.c
@@ -0,0 +1,17 @@
+/* Tries to create a file with a name that is much too long,
+   which must fail. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  static char name[512];
+  memset (name, 'x', sizeof name);
+  name[sizeof name - 1] = '\0';
+  
+  msg ("create(\"x...\"): %d", create (name, 0));
+}
diff --git a/src/tests/userprog/create-long.ck b/src/tests/userprog/create-long.ck
new file mode 100644
index 0000000..628411c
--- /dev/null
+++ b/src/tests/userprog/create-long.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(create-long) begin
+(create-long) create("x..."): 0
+(create-long) end
+create-long: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/create-normal.c b/src/tests/userprog/create-normal.c
new file mode 100644
index 0000000..3cbc463
--- /dev/null
+++ b/src/tests/userprog/create-normal.c
@@ -0,0 +1,10 @@
+/* Creates an ordinary empty file. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (create ("quux.dat", 0), "create quux.dat");
+}
diff --git a/src/tests/userprog/create-normal.ck b/src/tests/userprog/create-normal.ck
new file mode 100644
index 0000000..ca74a6e
--- /dev/null
+++ b/src/tests/userprog/create-normal.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(create-normal) begin
+(create-normal) create quux.dat
+(create-normal) end
+create-normal: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/create-null.c b/src/tests/userprog/create-null.c
new file mode 100644
index 0000000..287cb23
--- /dev/null
+++ b/src/tests/userprog/create-null.c
@@ -0,0 +1,11 @@
+/* Tries to create a file with the null pointer as its name.
+   The process must be terminated with exit code -1. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("create(NULL): %d", create (NULL, 0));
+}
diff --git a/src/tests/userprog/create-null.ck b/src/tests/userprog/create-null.ck
new file mode 100644
index 0000000..09b7872
--- /dev/null
+++ b/src/tests/userprog/create-null.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(create-null) begin
+create-null: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/exec-arg.c b/src/tests/userprog/exec-arg.c
new file mode 100644
index 0000000..82d0744
--- /dev/null
+++ b/src/tests/userprog/exec-arg.c
@@ -0,0 +1,10 @@
+/* Tests argument passing to child processes. */
+
+#include <syscall.h>
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  wait (exec ("child-args childarg"));
+}
diff --git a/src/tests/userprog/exec-arg.ck b/src/tests/userprog/exec-arg.ck
new file mode 100644
index 0000000..b7533ed
--- /dev/null
+++ b/src/tests/userprog/exec-arg.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(exec-arg) begin
+(args) begin
+(args) argc = 2
+(args) argv[0] = 'child-args'
+(args) argv[1] = 'childarg'
+(args) argv[2] = null
+(args) end
+child-args: exit(0)
+(exec-arg) end
+exec-arg: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/exec-bad-ptr.c b/src/tests/userprog/exec-bad-ptr.c
new file mode 100644
index 0000000..0abadd3
--- /dev/null
+++ b/src/tests/userprog/exec-bad-ptr.c
@@ -0,0 +1,11 @@
+/* Passes an invalid pointer to the exec system call.
+   The process must be terminated with -1 exit code. */
+
+#include <syscall.h>
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  exec ((char *) 0x20101234);
+}
diff --git a/src/tests/userprog/exec-bad-ptr.ck b/src/tests/userprog/exec-bad-ptr.ck
new file mode 100644
index 0000000..63f5f78
--- /dev/null
+++ b/src/tests/userprog/exec-bad-ptr.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(exec-bad-ptr) begin
+(exec-bad-ptr) end
+exec-bad-ptr: exit(0)
+EOF
+(exec-bad-ptr) begin
+exec-bad-ptr: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/exec-bound-2.c b/src/tests/userprog/exec-bound-2.c
new file mode 100644
index 0000000..7ebed61
--- /dev/null
+++ b/src/tests/userprog/exec-bound-2.c
@@ -0,0 +1,20 @@
+/* Invokes an exec system call with the exec string pointer argument
+   positioned such that only its first byte is valid  memory (bytes 1-3
+   of the pointer are invalid).  Must kill process. */
+
+#include <syscall-nr.h>
+#include "tests/userprog/boundary.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  char *p = get_bad_boundary () - 5;
+  *((int *) p) = SYS_EXEC;
+  p[4] = '!';
+
+  /* Invoke the system call. */
+  asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p));
+  fail ("should have killed process");
+}
diff --git a/src/tests/userprog/exec-bound-2.ck b/src/tests/userprog/exec-bound-2.ck
new file mode 100644
index 0000000..0be2946
--- /dev/null
+++ b/src/tests/userprog/exec-bound-2.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(exec-bound-2) begin
+exec-bound-2: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/exec-bound-3.c b/src/tests/userprog/exec-bound-3.c
new file mode 100644
index 0000000..41f1cd9
--- /dev/null
+++ b/src/tests/userprog/exec-bound-3.c
@@ -0,0 +1,28 @@
+/* Invokes an exec system call with the exec string straddling a
+   page boundary such that the first byte of the string is valid
+   but the remainder of the string is in invalid memory. Must
+   kill process. */
+
+#include <syscall-nr.h>
+#include "tests/userprog/boundary.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  char *p = get_bad_boundary () - 1;
+  *p = 'a';
+  exec(p);
+
+  /* Note: if this test fails to pass even with the official solutions,
+     it's probably because memory layout has changed and p no longer
+     refers to the proper page boundary. To fix the problem, uncomment
+     the line below to print out the boundary address. In addition,
+     add a printf line in load_segment to print out the address range
+     of each segment. From that, you'll be able to figure out how to
+     modify get_bad_boundary to make things work again. */
+
+  // msg("boundary address: 0x%x", p);
+  fail ("should have killed process");
+}
diff --git a/src/tests/userprog/exec-bound-3.ck b/src/tests/userprog/exec-bound-3.ck
new file mode 100644
index 0000000..93fed65
--- /dev/null
+++ b/src/tests/userprog/exec-bound-3.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(exec-bound-3) begin
+exec-bound-3: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/exec-bound.c b/src/tests/userprog/exec-bound.c
new file mode 100644
index 0000000..07a0366
--- /dev/null
+++ b/src/tests/userprog/exec-bound.c
@@ -0,0 +1,12 @@
+/* Exec a child with an exec string that spans a page boundary. */
+
+#include <syscall.h>
+#include "tests/userprog/boundary.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  wait (exec (copy_string_across_boundary("child-args arg1 arg2")));
+}
diff --git a/src/tests/userprog/exec-bound.ck b/src/tests/userprog/exec-bound.ck
new file mode 100644
index 0000000..62e588f
--- /dev/null
+++ b/src/tests/userprog/exec-bound.ck
@@ -0,0 +1,18 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(exec-bound) begin
+(args) begin
+(args) argc = 3
+(args) argv[0] = 'child-args'
+(args) argv[1] = 'arg1'
+(args) argv[2] = 'arg2'
+(args) argv[3] = null
+(args) end
+child-args: exit(0)
+(exec-bound) end
+exec-bound: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/exec-missing.c b/src/tests/userprog/exec-missing.c
new file mode 100644
index 0000000..bf08cad
--- /dev/null
+++ b/src/tests/userprog/exec-missing.c
@@ -0,0 +1,12 @@
+/* Tries to execute a nonexistent process.
+   The exec system call must return -1. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("exec(\"no-such-file\"): %d", exec ("no-such-file"));
+}
diff --git a/src/tests/userprog/exec-missing.ck b/src/tests/userprog/exec-missing.ck
new file mode 100644
index 0000000..0ef7aaa
--- /dev/null
+++ b/src/tests/userprog/exec-missing.ck
@@ -0,0 +1,31 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF', <<'EOF', <<'EOF']);
+(exec-missing) begin
+load: no-such-file: open failed
+(exec-missing) exec("no-such-file"): -1
+(exec-missing) end
+exec-missing: exit(0)
+EOF
+(exec-missing) begin
+(exec-missing) exec("no-such-file"): -1
+(exec-missing) end
+exec-missing: exit(0)
+EOF
+(exec-missing) begin
+load: no-such-file: open failed
+no-such-file: exit(-1)
+(exec-missing) exec("no-such-file"): -1
+(exec-missing) end
+exec-missing: exit(0)
+EOF
+(exec-missing) begin
+load: no-such-file: open failed
+(exec-missing) exec("no-such-file"): -1
+no-such-file: exit(-1)
+(exec-missing) end
+exec-missing: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/exec-multiple.c b/src/tests/userprog/exec-multiple.c
new file mode 100644
index 0000000..ba4c26e
--- /dev/null
+++ b/src/tests/userprog/exec-multiple.c
@@ -0,0 +1,14 @@
+/* Executes and waits for multiple child processes. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  wait (exec ("child-simple"));
+  wait (exec ("child-simple"));
+  wait (exec ("child-simple"));
+  wait (exec ("child-simple"));
+}
diff --git a/src/tests/userprog/exec-multiple.ck b/src/tests/userprog/exec-multiple.ck
new file mode 100644
index 0000000..99624cd
--- /dev/null
+++ b/src/tests/userprog/exec-multiple.ck
@@ -0,0 +1,18 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(exec-multiple) begin
+(child-simple) run
+child-simple: exit(81)
+(child-simple) run
+child-simple: exit(81)
+(child-simple) run
+child-simple: exit(81)
+(child-simple) run
+child-simple: exit(81)
+(exec-multiple) end
+exec-multiple: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/exec-once.c b/src/tests/userprog/exec-once.c
new file mode 100644
index 0000000..7bae5a1
--- /dev/null
+++ b/src/tests/userprog/exec-once.c
@@ -0,0 +1,11 @@
+/* Executes and waits for a single child process. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  wait (exec ("child-simple"));
+}
diff --git a/src/tests/userprog/exec-once.ck b/src/tests/userprog/exec-once.ck
new file mode 100644
index 0000000..00b59ed
--- /dev/null
+++ b/src/tests/userprog/exec-once.ck
@@ -0,0 +1,12 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(exec-once) begin
+(child-simple) run
+child-simple: exit(81)
+(exec-once) end
+exec-once: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/exit.c b/src/tests/userprog/exit.c
new file mode 100644
index 0000000..cb4eb8f
--- /dev/null
+++ b/src/tests/userprog/exit.c
@@ -0,0 +1,11 @@
+/* Tests the exit system call. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  exit (57);
+  fail ("should have called exit(57)");
+}
diff --git a/src/tests/userprog/exit.ck b/src/tests/userprog/exit.ck
new file mode 100644
index 0000000..a552702
--- /dev/null
+++ b/src/tests/userprog/exit.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(exit) begin
+exit: exit(57)
+EOF
+pass;
diff --git a/src/tests/userprog/halt.c b/src/tests/userprog/halt.c
new file mode 100644
index 0000000..4a99bce
--- /dev/null
+++ b/src/tests/userprog/halt.c
@@ -0,0 +1,11 @@
+/* Tests the halt system call. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  halt ();
+  fail ("should have halted");
+}
diff --git a/src/tests/userprog/halt.ck b/src/tests/userprog/halt.ck
new file mode 100644
index 0000000..1b701ed
--- /dev/null
+++ b/src/tests/userprog/halt.ck
@@ -0,0 +1,15 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+
+our ($test);
+my (@output) = read_text_file ("$test.output");
+
+common_checks ("run", @output);
+
+fail "missing 'begin' message\n"
+  if !grep ($_ eq '(halt) begin', @output);
+fail "found 'fail' message--halt didn't really halt\n"
+  if grep ($_ eq '(halt) fail', @output);
+pass;
diff --git a/src/tests/userprog/lib/.gitignore b/src/tests/userprog/lib/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/src/tests/userprog/lib/.gitignore
@@ -0,0 +1 @@
+*.d
diff --git a/src/tests/userprog/lib/user/.dummy b/src/tests/userprog/lib/user/.dummy
new file mode 100644
index 0000000..e69de29
diff --git a/src/tests/userprog/lib/user/.gitignore b/src/tests/userprog/lib/user/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/src/tests/userprog/lib/user/.gitignore
@@ -0,0 +1 @@
+*.d
diff --git a/src/tests/userprog/multi-child-fd.c b/src/tests/userprog/multi-child-fd.c
new file mode 100644
index 0000000..48de4b4
--- /dev/null
+++ b/src/tests/userprog/multi-child-fd.c
@@ -0,0 +1,25 @@
+/* Opens a file and then runs a subprocess that tries to close
+   the file.  (Pintos does not have inheritance of file handles,
+   so this must fail.)  The parent process then attempts to use
+   the file handle, which must succeed. */
+
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/userprog/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  char child_cmd[128];
+  int handle;
+
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+
+  snprintf (child_cmd, sizeof child_cmd, "child-close %d", handle);
+  
+  msg ("wait(exec()) = %d", wait (exec (child_cmd)));
+
+  check_file_handle (handle, "sample.txt", sample, sizeof sample - 1);
+}
diff --git a/src/tests/userprog/multi-child-fd.ck b/src/tests/userprog/multi-child-fd.ck
new file mode 100644
index 0000000..d0b3a33
--- /dev/null
+++ b/src/tests/userprog/multi-child-fd.ck
@@ -0,0 +1,25 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(multi-child-fd) begin
+(multi-child-fd) open "sample.txt"
+(child-close) begin
+(child-close) end
+child-close: exit(0)
+(multi-child-fd) wait(exec()) = 0
+(multi-child-fd) verified contents of "sample.txt"
+(multi-child-fd) end
+multi-child-fd: exit(0)
+EOF
+(multi-child-fd) begin
+(multi-child-fd) open "sample.txt"
+(child-close) begin
+child-close: exit(-1)
+(multi-child-fd) wait(exec()) = -1
+(multi-child-fd) verified contents of "sample.txt"
+(multi-child-fd) end
+multi-child-fd: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/multi-recurse.c b/src/tests/userprog/multi-recurse.c
new file mode 100644
index 0000000..fcd592b
--- /dev/null
+++ b/src/tests/userprog/multi-recurse.c
@@ -0,0 +1,34 @@
+/* Executes itself recursively to the depth indicated by the
+   first command-line argument. */
+
+#include <debug.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/lib.h"
+
+int
+main (int argc UNUSED, char *argv[]) 
+{
+  int n = atoi (argv[1]);
+
+  test_name = "multi-recurse";
+
+  msg ("begin %d", n);
+  if (n != 0) 
+    {
+      char child_cmd[128];
+      pid_t child_pid;
+      int code;
+      
+      snprintf (child_cmd, sizeof child_cmd, "multi-recurse %d", n - 1);
+      CHECK ((child_pid = exec (child_cmd)) != -1, "exec(\"%s\")", child_cmd);
+
+      code = wait (child_pid);
+      if (code != n - 1)
+        fail ("wait(exec(\"%s\")) returned %d", child_cmd, code);
+    }
+  
+  msg ("end %d", n);
+  return n;
+}
diff --git a/src/tests/userprog/multi-recurse.ck b/src/tests/userprog/multi-recurse.ck
new file mode 100644
index 0000000..41eb4a6
--- /dev/null
+++ b/src/tests/userprog/multi-recurse.ck
@@ -0,0 +1,70 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(multi-recurse) begin 15
+(multi-recurse) exec("multi-recurse 14")
+(multi-recurse) begin 14
+(multi-recurse) exec("multi-recurse 13")
+(multi-recurse) begin 13
+(multi-recurse) exec("multi-recurse 12")
+(multi-recurse) begin 12
+(multi-recurse) exec("multi-recurse 11")
+(multi-recurse) begin 11
+(multi-recurse) exec("multi-recurse 10")
+(multi-recurse) begin 10
+(multi-recurse) exec("multi-recurse 9")
+(multi-recurse) begin 9
+(multi-recurse) exec("multi-recurse 8")
+(multi-recurse) begin 8
+(multi-recurse) exec("multi-recurse 7")
+(multi-recurse) begin 7
+(multi-recurse) exec("multi-recurse 6")
+(multi-recurse) begin 6
+(multi-recurse) exec("multi-recurse 5")
+(multi-recurse) begin 5
+(multi-recurse) exec("multi-recurse 4")
+(multi-recurse) begin 4
+(multi-recurse) exec("multi-recurse 3")
+(multi-recurse) begin 3
+(multi-recurse) exec("multi-recurse 2")
+(multi-recurse) begin 2
+(multi-recurse) exec("multi-recurse 1")
+(multi-recurse) begin 1
+(multi-recurse) exec("multi-recurse 0")
+(multi-recurse) begin 0
+(multi-recurse) end 0
+multi-recurse: exit(0)
+(multi-recurse) end 1
+multi-recurse: exit(1)
+(multi-recurse) end 2
+multi-recurse: exit(2)
+(multi-recurse) end 3
+multi-recurse: exit(3)
+(multi-recurse) end 4
+multi-recurse: exit(4)
+(multi-recurse) end 5
+multi-recurse: exit(5)
+(multi-recurse) end 6
+multi-recurse: exit(6)
+(multi-recurse) end 7
+multi-recurse: exit(7)
+(multi-recurse) end 8
+multi-recurse: exit(8)
+(multi-recurse) end 9
+multi-recurse: exit(9)
+(multi-recurse) end 10
+multi-recurse: exit(10)
+(multi-recurse) end 11
+multi-recurse: exit(11)
+(multi-recurse) end 12
+multi-recurse: exit(12)
+(multi-recurse) end 13
+multi-recurse: exit(13)
+(multi-recurse) end 14
+multi-recurse: exit(14)
+(multi-recurse) end 15
+multi-recurse: exit(15)
+EOF
+pass;
diff --git a/src/tests/userprog/no-vm/Make.tests b/src/tests/userprog/no-vm/Make.tests
new file mode 100644
index 0000000..a545e18
--- /dev/null
+++ b/src/tests/userprog/no-vm/Make.tests
@@ -0,0 +1,8 @@
+# -*- makefile -*-
+
+tests/userprog/no-vm_TESTS = tests/userprog/no-vm/multi-oom
+tests/userprog/no-vm_PROGS = $(tests/userprog/no-vm_TESTS)
+tests/userprog/no-vm/multi-oom_SRC = tests/userprog/no-vm/multi-oom.c	\
+tests/lib.c
+
+tests/userprog/no-vm/multi-oom.output: TIMEOUT = 360
diff --git a/src/tests/userprog/no-vm/Rubric b/src/tests/userprog/no-vm/Rubric
new file mode 100644
index 0000000..c3816c6
--- /dev/null
+++ b/src/tests/userprog/no-vm/Rubric
@@ -0,0 +1,3 @@
+Functionality of features that VM might break:
+
+1	multi-oom
diff --git a/src/tests/userprog/no-vm/multi-oom.c b/src/tests/userprog/no-vm/multi-oom.c
new file mode 100644
index 0000000..be0668a
--- /dev/null
+++ b/src/tests/userprog/no-vm/multi-oom.c
@@ -0,0 +1,179 @@
+/* Recursively executes itself until the child fails to execute.
+   We expect that at least 30 copies can run.
+
+   We count how many children your kernel was able to execute
+   before it fails to start a new process.  We require that,
+   if a process doesn't actually get to start, exec() must
+   return -1, not a valid PID.
+
+   We repeat this process 10 times, checking that your kernel
+   allows for the same level of depth every time.
+
+   In addition, some processes will spawn children that terminate
+   abnormally after allocating some resources.
+
+   Written by Godmar Back <godmar@gmail.com>
+ */
+
+#include <debug.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <syscall.h>
+#include <random.h>
+#include "tests/lib.h"
+
+static const int EXPECTED_DEPTH_TO_PASS = 30;
+static const int EXPECTED_REPETITIONS = 10;
+
+enum child_termination_mode { RECURSE, CRASH };
+
+/* Spawn a recursive copy of ourselves, passing along instructions
+   for the child. */
+static pid_t
+spawn_child (int c, enum child_termination_mode mode)
+{
+  char child_cmd[128];
+  snprintf (child_cmd, sizeof child_cmd,
+            "%s %d %s", test_name, c, mode == CRASH ? "-k" : "");
+  return exec (child_cmd);
+}
+
+/* Open a number of files (and fail to close them).
+   The kernel must free any kernel resources associated
+   with these file descriptors. */
+static void
+consume_some_resources (void)
+{
+  int fd, fdmax = 126;
+
+  /* Open as many files as we can, up to fdmax.
+     Depending on how file descriptors are allocated inside
+     the kernel, open() may fail if the kernel is low on memory.
+     A low-memory condition in open() should not lead to the
+     termination of the process.  */
+  for (fd = 0; fd < fdmax; fd++)
+    if (open (test_name) == -1)
+      break;
+}
+
+/* Consume some resources, then terminate this process
+   in some abnormal way.  */
+static int NO_INLINE
+consume_some_resources_and_die (int seed)
+{
+  consume_some_resources ();
+  random_init (seed);
+  volatile int *PHYS_BASE = (volatile int *)0xC0000000;
+
+  switch (random_ulong () % 5)
+    {
+      case 0:
+        *(volatile int *) NULL = 42;
+
+      case 1:
+        return *(volatile int *) NULL;
+
+      case 2:
+        return *PHYS_BASE;
+
+      case 3:
+        *PHYS_BASE = 42;
+
+      case 4:
+        open ((char *)PHYS_BASE);
+        exit (-1);
+
+      default:
+        NOT_REACHED ();
+    }
+  return 0;
+}
+
+/* The first copy is invoked without command line arguments.
+   Subsequent copies are invoked with a parameter 'depth'
+   that describes how many parent processes preceded them.
+   Each process spawns one or multiple recursive copies of
+   itself, passing 'depth+1' as depth.
+
+   Some children are started with the '-k' flag, which will
+   result in abnormal termination.
+ */
+int
+main (int argc, char *argv[])
+{
+  int n;
+
+  test_name = "multi-oom";
+
+  n = argc > 1 ? atoi (argv[1]) : 0;
+  bool is_at_root = (n == 0);
+  if (is_at_root)
+    msg ("begin");
+
+  /* If -k is passed, crash this process. */
+  if (argc > 2 && !strcmp(argv[2], "-k"))
+    {
+      consume_some_resources_and_die (n);
+      NOT_REACHED ();
+    }
+
+  int howmany = is_at_root ? EXPECTED_REPETITIONS : 1;
+  int i, expected_depth = -1;
+
+  for (i = 0; i < howmany; i++)
+    {
+      pid_t child_pid;
+
+      /* Spawn a child that will be abnormally terminated.
+         To speed the test up, do this only for processes
+         spawned at a certain depth. */
+      if (n > EXPECTED_DEPTH_TO_PASS/2)
+        {
+          child_pid = spawn_child (n + 1, CRASH);
+          if (child_pid != -1)
+            {
+              if (wait (child_pid) != -1)
+                fail ("crashed child should return -1.");
+            }
+          /* If spawning this child failed, so should
+             the next spawn_child below. */
+        }
+
+      /* Now spawn the child that will recurse. */
+      child_pid = spawn_child (n + 1, RECURSE);
+
+      /* If maximum depth is reached, return result. */
+      if (child_pid == -1)
+        return n;
+
+      /* Else wait for child to report how deeply it was able to recurse. */
+      int reached_depth = wait (child_pid);
+      if (reached_depth == -1)
+        fail ("wait returned -1.");
+
+      /* Record the depth reached during the first run; on subsequent
+         runs, fail if those runs do not match the depth achieved on the
+         first run. */
+      if (i == 0)
+        expected_depth = reached_depth;
+      else if (expected_depth != reached_depth)
+        fail ("after run %d/%d, expected depth %d, actual depth %d.",
+              i, howmany, expected_depth, reached_depth);
+      ASSERT (expected_depth == reached_depth);
+    }
+
+  consume_some_resources ();
+
+  if (n == 0)
+    {
+      if (expected_depth < EXPECTED_DEPTH_TO_PASS)
+        fail ("should have forked at least %d times.", EXPECTED_DEPTH_TO_PASS);
+      msg ("success. program forked %d times.", howmany);
+      msg ("end");
+    }
+
+  return expected_depth;
+}
+// vim: sw=2
diff --git a/src/tests/userprog/no-vm/multi-oom.ck b/src/tests/userprog/no-vm/multi-oom.ck
new file mode 100644
index 0000000..59a0bcd
--- /dev/null
+++ b/src/tests/userprog/no-vm/multi-oom.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_USER_FAULTS => 1, IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(multi-oom) begin
+(multi-oom) success. program forked 10 times.
+(multi-oom) end
+EOF
+pass;
diff --git a/src/tests/userprog/null.ck b/src/tests/userprog/null.ck
new file mode 100644
index 0000000..980de35
--- /dev/null
+++ b/src/tests/userprog/null.ck
@@ -0,0 +1,8 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+system call!
+EOF
+pass;
diff --git a/src/tests/userprog/open-bad-ptr.c b/src/tests/userprog/open-bad-ptr.c
new file mode 100644
index 0000000..9cd4edf
--- /dev/null
+++ b/src/tests/userprog/open-bad-ptr.c
@@ -0,0 +1,13 @@
+/* Passes an invalid pointer to the open system call.
+   The process must be terminated with -1 exit code. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("open(0x20101234): %d", open ((char *) 0x20101234));
+  fail ("should have called exit(-1)");
+}
diff --git a/src/tests/userprog/open-bad-ptr.ck b/src/tests/userprog/open-bad-ptr.ck
new file mode 100644
index 0000000..45349e2
--- /dev/null
+++ b/src/tests/userprog/open-bad-ptr.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(open-bad-ptr) begin
+(open-bad-ptr) end
+open-bad-ptr: exit(0)
+EOF
+(open-bad-ptr) begin
+open-bad-ptr: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/open-boundary.c b/src/tests/userprog/open-boundary.c
new file mode 100644
index 0000000..cc8ff8b
--- /dev/null
+++ b/src/tests/userprog/open-boundary.c
@@ -0,0 +1,14 @@
+/* Creates a file whose name spans the boundary between two pages.
+   This is valid, so it must succeed. */
+
+#include <syscall.h>
+#include "tests/userprog/boundary.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (open (copy_string_across_boundary ("sample.txt")) > 1,
+         "open \"sample.txt\"");
+}
diff --git a/src/tests/userprog/open-boundary.ck b/src/tests/userprog/open-boundary.ck
new file mode 100644
index 0000000..8060d22
--- /dev/null
+++ b/src/tests/userprog/open-boundary.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(open-boundary) begin
+(open-boundary) open "sample.txt"
+(open-boundary) end
+open-boundary: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/open-empty.c b/src/tests/userprog/open-empty.c
new file mode 100644
index 0000000..3ea9907
--- /dev/null
+++ b/src/tests/userprog/open-empty.c
@@ -0,0 +1,13 @@
+/* Tries to open a file with the empty string as its name. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle = open ("");
+  if (handle != -1)
+    fail ("open() returned %d instead of -1", handle);
+}
diff --git a/src/tests/userprog/open-empty.ck b/src/tests/userprog/open-empty.ck
new file mode 100644
index 0000000..885fb41
--- /dev/null
+++ b/src/tests/userprog/open-empty.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(open-empty) begin
+(open-empty) end
+open-empty: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/open-missing.c b/src/tests/userprog/open-missing.c
new file mode 100644
index 0000000..13ecbda
--- /dev/null
+++ b/src/tests/userprog/open-missing.c
@@ -0,0 +1,13 @@
+/* Tries to open a nonexistent file. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle = open ("no-such-file");
+  if (handle != -1)
+    fail ("open() returned %d", handle);
+}
diff --git a/src/tests/userprog/open-missing.ck b/src/tests/userprog/open-missing.ck
new file mode 100644
index 0000000..d72d878
--- /dev/null
+++ b/src/tests/userprog/open-missing.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(open-missing) begin
+(open-missing) end
+open-missing: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/open-normal.c b/src/tests/userprog/open-normal.c
new file mode 100644
index 0000000..5132465
--- /dev/null
+++ b/src/tests/userprog/open-normal.c
@@ -0,0 +1,13 @@
+/* Open a file. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle = open ("sample.txt");
+  if (handle < 2)
+    fail ("open() returned %d", handle);
+}
diff --git a/src/tests/userprog/open-normal.ck b/src/tests/userprog/open-normal.ck
new file mode 100644
index 0000000..4f6c342
--- /dev/null
+++ b/src/tests/userprog/open-normal.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(open-normal) begin
+(open-normal) end
+open-normal: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/open-null.c b/src/tests/userprog/open-null.c
new file mode 100644
index 0000000..bb418b8
--- /dev/null
+++ b/src/tests/userprog/open-null.c
@@ -0,0 +1,12 @@
+/* Tries to open a file with the null pointer as its name.
+   The process must be terminated with exit code -1. */
+
+#include <stddef.h>
+#include <syscall.h>
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  open (NULL);
+}
diff --git a/src/tests/userprog/open-null.ck b/src/tests/userprog/open-null.ck
new file mode 100644
index 0000000..b4a3bcb
--- /dev/null
+++ b/src/tests/userprog/open-null.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(open-null) begin
+(open-null) end
+open-null: exit(0)
+EOF
+(open-null) begin
+open-null: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/open-twice.c b/src/tests/userprog/open-twice.c
new file mode 100644
index 0000000..dd333af
--- /dev/null
+++ b/src/tests/userprog/open-twice.c
@@ -0,0 +1,19 @@
+/* Tries to open the same file twice,
+   which must succeed and must return a different file descriptor
+   in each case. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int h1 = open ("sample.txt");
+  int h2 = open ("sample.txt");  
+
+  CHECK ((h1 = open ("sample.txt")) > 1, "open \"sample.txt\" once");
+  CHECK ((h2 = open ("sample.txt")) > 1, "open \"sample.txt\" again");
+  if (h1 == h2)
+    fail ("open() returned %d both times", h1);
+}
diff --git a/src/tests/userprog/open-twice.ck b/src/tests/userprog/open-twice.ck
new file mode 100644
index 0000000..64fa805
--- /dev/null
+++ b/src/tests/userprog/open-twice.ck
@@ -0,0 +1,12 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(open-twice) begin
+(open-twice) open "sample.txt" once
+(open-twice) open "sample.txt" again
+(open-twice) end
+open-twice: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/read-bad-fd.c b/src/tests/userprog/read-bad-fd.c
new file mode 100644
index 0000000..a8b190d
--- /dev/null
+++ b/src/tests/userprog/read-bad-fd.c
@@ -0,0 +1,21 @@
+/* Tries to read from an invalid fd,
+   which must either fail silently or terminate the process with
+   exit code -1. */
+
+#include <limits.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  char buf;
+  read (0x20101234, &buf, 1);
+  read (5, &buf, 1);
+  read (1234, &buf, 1);
+  read (-1, &buf, 1);
+  read (-1024, &buf, 1);
+  read (INT_MIN, &buf, 1);
+  read (INT_MAX, &buf, 1);
+}
diff --git a/src/tests/userprog/read-bad-fd.ck b/src/tests/userprog/read-bad-fd.ck
new file mode 100644
index 0000000..5fedcc7
--- /dev/null
+++ b/src/tests/userprog/read-bad-fd.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(read-bad-fd) begin
+(read-bad-fd) end
+read-bad-fd: exit(0)
+EOF
+(read-bad-fd) begin
+read-bad-fd: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/read-bad-ptr.c b/src/tests/userprog/read-bad-ptr.c
new file mode 100644
index 0000000..8fe756e
--- /dev/null
+++ b/src/tests/userprog/read-bad-ptr.c
@@ -0,0 +1,16 @@
+/* Passes an invalid pointer to the read system call.
+   The process must be terminated with -1 exit code. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle;
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+
+  read (handle, (char *) 0xc0100000, 123);
+  fail ("should not have survived read()");
+}
diff --git a/src/tests/userprog/read-bad-ptr.ck b/src/tests/userprog/read-bad-ptr.ck
new file mode 100644
index 0000000..d10accf
--- /dev/null
+++ b/src/tests/userprog/read-bad-ptr.ck
@@ -0,0 +1,15 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(read-bad-ptr) begin
+(read-bad-ptr) open "sample.txt"
+(read-bad-ptr) end
+read-bad-ptr: exit(0)
+EOF
+(read-bad-ptr) begin
+(read-bad-ptr) open "sample.txt"
+read-bad-ptr: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/read-boundary.c b/src/tests/userprog/read-boundary.c
new file mode 100644
index 0000000..9c19966
--- /dev/null
+++ b/src/tests/userprog/read-boundary.c
@@ -0,0 +1,30 @@
+/* Reads data spanning two pages in virtual address space,
+   which must succeed. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/userprog/boundary.h"
+#include "tests/userprog/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle;
+  int byte_cnt;
+  char *buffer;
+
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+
+  buffer = get_boundary_area () - sizeof sample / 2;
+  byte_cnt = read (handle, buffer, sizeof sample - 1);
+  if (byte_cnt != sizeof sample - 1)
+    fail ("read() returned %d instead of %zu", byte_cnt, sizeof sample - 1);
+  else if (strcmp (sample, buffer)) 
+    {
+      msg ("expected text:\n%s", sample);
+      msg ("text actually read:\n%s", buffer);
+      fail ("expected text differs from actual");
+    }
+}
diff --git a/src/tests/userprog/read-boundary.ck b/src/tests/userprog/read-boundary.ck
new file mode 100644
index 0000000..08dc161
--- /dev/null
+++ b/src/tests/userprog/read-boundary.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(read-boundary) begin
+(read-boundary) open "sample.txt"
+(read-boundary) end
+read-boundary: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/read-normal.c b/src/tests/userprog/read-normal.c
new file mode 100644
index 0000000..16d15cc
--- /dev/null
+++ b/src/tests/userprog/read-normal.c
@@ -0,0 +1,11 @@
+/* Try reading a file in the most normal way. */
+
+#include "tests/userprog/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  check_file ("sample.txt", sample, sizeof sample - 1);
+}
diff --git a/src/tests/userprog/read-normal.ck b/src/tests/userprog/read-normal.ck
new file mode 100644
index 0000000..0ed2998
--- /dev/null
+++ b/src/tests/userprog/read-normal.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(read-normal) begin
+(read-normal) open "sample.txt" for verification
+(read-normal) verified contents of "sample.txt"
+(read-normal) close "sample.txt"
+(read-normal) end
+read-normal: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/read-stdout.c b/src/tests/userprog/read-stdout.c
new file mode 100644
index 0000000..d0630b9
--- /dev/null
+++ b/src/tests/userprog/read-stdout.c
@@ -0,0 +1,14 @@
+/* Try reading from fd 1 (stdout), 
+   which may just fail or terminate the process with -1 exit
+   code. */
+
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  char buf;
+  read (STDOUT_FILENO, &buf, 1);
+}
diff --git a/src/tests/userprog/read-stdout.ck b/src/tests/userprog/read-stdout.ck
new file mode 100644
index 0000000..7d87b52
--- /dev/null
+++ b/src/tests/userprog/read-stdout.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(read-stdout) begin
+(read-stdout) end
+read-stdout: exit(0)
+EOF
+(read-stdout) begin
+read-stdout: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/read-zero.c b/src/tests/userprog/read-zero.c
new file mode 100644
index 0000000..e441817
--- /dev/null
+++ b/src/tests/userprog/read-zero.c
@@ -0,0 +1,22 @@
+/* Try a 0-byte read, which should return 0 without reading
+   anything. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle, byte_cnt;
+  char buf;
+
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+
+  buf = 123;
+  byte_cnt = read (handle, &buf, 0);
+  if (byte_cnt != 0)
+    fail ("read() returned %d instead of 0", byte_cnt);
+  else if (buf != 123)
+    fail ("0-byte read() modified buffer");
+}
diff --git a/src/tests/userprog/read-zero.ck b/src/tests/userprog/read-zero.ck
new file mode 100644
index 0000000..8346dbc
--- /dev/null
+++ b/src/tests/userprog/read-zero.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(read-zero) begin
+(read-zero) open "sample.txt"
+(read-zero) end
+read-zero: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/rox-child.c b/src/tests/userprog/rox-child.c
new file mode 100644
index 0000000..30afba2
--- /dev/null
+++ b/src/tests/userprog/rox-child.c
@@ -0,0 +1,5 @@
+/* Ensure that the executable of a running process cannot be
+   modified, even by a child process. */
+
+#define CHILD_CNT "1"
+#include "tests/userprog/rox-child.inc"
diff --git a/src/tests/userprog/rox-child.ck b/src/tests/userprog/rox-child.ck
new file mode 100644
index 0000000..e6363fb
--- /dev/null
+++ b/src/tests/userprog/rox-child.ck
@@ -0,0 +1,20 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(rox-child) begin
+(rox-child) open "child-rox"
+(rox-child) read "child-rox"
+(rox-child) write "child-rox"
+(rox-child) exec "child-rox 1"
+(child-rox) begin
+(child-rox) try to write "child-rox"
+(child-rox) try to write "child-rox"
+(child-rox) end
+child-rox: exit(12)
+(rox-child) write "child-rox"
+(rox-child) end
+rox-child: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/rox-child.inc b/src/tests/userprog/rox-child.inc
new file mode 100644
index 0000000..1e2ade9
--- /dev/null
+++ b/src/tests/userprog/rox-child.inc
@@ -0,0 +1,33 @@
+/* -*- c -*- */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  const char *child_cmd = "child-rox " CHILD_CNT;
+  int handle;
+  pid_t child;
+  char buffer[16];
+
+  /* Open child-rox, read from it, write back same data. */
+  CHECK ((handle = open ("child-rox")) > 1, "open \"child-rox\"");
+  CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer,
+         "read \"child-rox\"");
+  seek (handle, 0);
+  CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer,
+         "write \"child-rox\"");
+
+  /* Execute child-rox and wait for it. */
+  CHECK ((child = exec (child_cmd)) != -1, "exec \"%s\"", child_cmd);
+  quiet = true;
+  CHECK (wait (child) == 12, "wait for child");
+  quiet = false;
+
+  /* Write to child-rox again. */
+  seek (handle, 0);
+  CHECK (write (handle, buffer, sizeof buffer) == (int) sizeof buffer,
+         "write \"child-rox\"");
+}
diff --git a/src/tests/userprog/rox-multichild.c b/src/tests/userprog/rox-multichild.c
new file mode 100644
index 0000000..8e74dab
--- /dev/null
+++ b/src/tests/userprog/rox-multichild.c
@@ -0,0 +1,5 @@
+/* Ensure that the executable of a running process cannot be
+   modified, even in the presence of multiple children. */
+
+#define CHILD_CNT "5"
+#include "tests/userprog/rox-child.inc"
diff --git a/src/tests/userprog/rox-multichild.ck b/src/tests/userprog/rox-multichild.ck
new file mode 100644
index 0000000..14b27db
--- /dev/null
+++ b/src/tests/userprog/rox-multichild.ck
@@ -0,0 +1,44 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(rox-multichild) begin
+(rox-multichild) open "child-rox"
+(rox-multichild) read "child-rox"
+(rox-multichild) write "child-rox"
+(rox-multichild) exec "child-rox 5"
+(child-rox) begin
+(child-rox) try to write "child-rox"
+(child-rox) exec "child-rox 4"
+(child-rox) begin
+(child-rox) try to write "child-rox"
+(child-rox) exec "child-rox 3"
+(child-rox) begin
+(child-rox) try to write "child-rox"
+(child-rox) exec "child-rox 2"
+(child-rox) begin
+(child-rox) try to write "child-rox"
+(child-rox) exec "child-rox 1"
+(child-rox) begin
+(child-rox) try to write "child-rox"
+(child-rox) try to write "child-rox"
+(child-rox) end
+child-rox: exit(12)
+(child-rox) try to write "child-rox"
+(child-rox) end
+child-rox: exit(12)
+(child-rox) try to write "child-rox"
+(child-rox) end
+child-rox: exit(12)
+(child-rox) try to write "child-rox"
+(child-rox) end
+child-rox: exit(12)
+(child-rox) try to write "child-rox"
+(child-rox) end
+child-rox: exit(12)
+(rox-multichild) write "child-rox"
+(rox-multichild) end
+rox-multichild: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/rox-simple.c b/src/tests/userprog/rox-simple.c
new file mode 100644
index 0000000..e84a064
--- /dev/null
+++ b/src/tests/userprog/rox-simple.c
@@ -0,0 +1,19 @@
+/* Ensure that the executable of a running process cannot be
+   modified. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle;
+  char buffer[16];
+  
+  CHECK ((handle = open ("rox-simple")) > 1, "open \"rox-simple\"");
+  CHECK (read (handle, buffer, sizeof buffer) == (int) sizeof buffer,
+         "read \"rox-simple\"");
+  CHECK (write (handle, buffer, sizeof buffer) == 0,
+         "try to write \"rox-simple\"");
+}
diff --git a/src/tests/userprog/rox-simple.ck b/src/tests/userprog/rox-simple.ck
new file mode 100644
index 0000000..c9dcc66
--- /dev/null
+++ b/src/tests/userprog/rox-simple.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(rox-simple) begin
+(rox-simple) open "rox-simple"
+(rox-simple) read "rox-simple"
+(rox-simple) try to write "rox-simple"
+(rox-simple) end
+rox-simple: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/sample.inc b/src/tests/userprog/sample.inc
new file mode 100644
index 0000000..59f2bcb
--- /dev/null
+++ b/src/tests/userprog/sample.inc
@@ -0,0 +1,6 @@
+char sample[] = {
+  "\"Amazing Electronic Fact: If you scuffed your feet long enough without\n"
+  " touching anything, you would build up so many electrons that your\n"
+  " finger would explode!  But this is nothing to worry about unless you\n"
+  " have carpeting.\" --Dave Barry\n" 
+};
diff --git a/src/tests/userprog/sample.txt b/src/tests/userprog/sample.txt
new file mode 100644
index 0000000..5050fec
--- /dev/null
+++ b/src/tests/userprog/sample.txt
@@ -0,0 +1,4 @@
+"Amazing Electronic Fact: If you scuffed your feet long enough without
+ touching anything, you would build up so many electrons that your
+ finger would explode!  But this is nothing to worry about unless you
+ have carpeting." --Dave Barry
diff --git a/src/tests/userprog/sc-bad-arg.c b/src/tests/userprog/sc-bad-arg.c
new file mode 100644
index 0000000..0b512a0
--- /dev/null
+++ b/src/tests/userprog/sc-bad-arg.c
@@ -0,0 +1,17 @@
+/* Sticks a system call number (SYS_EXIT) at the very top of the
+   stack, then invokes a system call with the stack pointer
+   (%esp) set to its address.  The process must be terminated
+   with -1 exit code because the argument to the system call
+   would be above the top of the user address space. */
+
+#include <syscall-nr.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  asm volatile ("movl $0xbffffffc, %%esp; movl %0, (%%esp); int $0x30"
+                : : "i" (SYS_EXIT));
+  fail ("should have called exit(-1)");
+}
diff --git a/src/tests/userprog/sc-bad-arg.ck b/src/tests/userprog/sc-bad-arg.ck
new file mode 100644
index 0000000..8981105
--- /dev/null
+++ b/src/tests/userprog/sc-bad-arg.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(sc-bad-arg) begin
+sc-bad-arg: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/sc-bad-sp.c b/src/tests/userprog/sc-bad-sp.c
new file mode 100644
index 0000000..39cce84
--- /dev/null
+++ b/src/tests/userprog/sc-bad-sp.c
@@ -0,0 +1,20 @@
+/* Invokes a system call with the stack pointer (%esp) set to a
+   bad address.  The process must be terminated with -1 exit
+   code. 
+
+   For Project 3: The bad address lies approximately 64MB below
+   the code segment, so there is no ambiguity that this attempt
+   must be rejected even after stack growth is implemented.
+   Moreover, a good stack growth heuristics should probably not
+   grow the stack for the purpose of reading the system call
+   number and arguments. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  asm volatile ("movl $.-(64*1024*1024), %esp; int $0x30");
+  fail ("should have called exit(-1)");
+}
diff --git a/src/tests/userprog/sc-bad-sp.ck b/src/tests/userprog/sc-bad-sp.ck
new file mode 100644
index 0000000..498cec1
--- /dev/null
+++ b/src/tests/userprog/sc-bad-sp.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(sc-bad-sp) begin
+sc-bad-sp: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/sc-boundary-2.c b/src/tests/userprog/sc-boundary-2.c
new file mode 100644
index 0000000..8acf036
--- /dev/null
+++ b/src/tests/userprog/sc-boundary-2.c
@@ -0,0 +1,22 @@
+/* Invokes a system call with one byte of the system call's
+   argument on a separate page from the rest of the bytes.  This
+   must work. */
+
+#include <syscall-nr.h>
+#include "tests/userprog/boundary.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  /* Make one byte of a syscall argument hang over into a second
+     page. */
+  int *p = (int *) ((char *) get_boundary_area () - 7);
+  p[0] = SYS_EXIT;
+  p[1] = 67;
+
+  /* Invoke the system call. */
+  asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p));
+  fail ("should have called exit(67)");
+}
diff --git a/src/tests/userprog/sc-boundary-2.ck b/src/tests/userprog/sc-boundary-2.ck
new file mode 100644
index 0000000..43766bf
--- /dev/null
+++ b/src/tests/userprog/sc-boundary-2.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(sc-boundary-2) begin
+sc-boundary-2: exit(67)
+EOF
+pass;
diff --git a/src/tests/userprog/sc-boundary-3.c b/src/tests/userprog/sc-boundary-3.c
new file mode 100644
index 0000000..b2e24ca
--- /dev/null
+++ b/src/tests/userprog/sc-boundary-3.c
@@ -0,0 +1,20 @@
+/* Invokes a system call with the system call number positioned
+   such that its first byte is valid but the remaining bytes of
+   the number are in invalid memory. Must kill process. */
+
+#include <syscall-nr.h>
+#include "tests/userprog/boundary.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  char *p = get_bad_boundary ();
+  p--;
+  *p = 100;
+
+  /* Invoke the system call. */
+  asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p));
+  fail ("should have killed process");
+}
diff --git a/src/tests/userprog/sc-boundary-3.ck b/src/tests/userprog/sc-boundary-3.ck
new file mode 100644
index 0000000..a7ac8c3
--- /dev/null
+++ b/src/tests/userprog/sc-boundary-3.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(sc-boundary-3) begin
+sc-boundary-3: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/sc-boundary.c b/src/tests/userprog/sc-boundary.c
new file mode 100644
index 0000000..d889535
--- /dev/null
+++ b/src/tests/userprog/sc-boundary.c
@@ -0,0 +1,22 @@
+/* Invokes a system call with the system call number and its
+   argument on separate pages.  This must work. */
+
+#include <syscall-nr.h>
+#include "tests/userprog/boundary.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  /* Put a syscall number at the end of one page
+     and its argument at the beginning of another. */
+  int *p = get_boundary_area ();
+  p--;
+  p[0] = SYS_EXIT;
+  p[1] = 42;
+
+  /* Invoke the system call. */
+  asm volatile ("movl %0, %%esp; int $0x30" : : "g" (p));
+  fail ("should have called exit(42)");
+}
diff --git a/src/tests/userprog/sc-boundary.ck b/src/tests/userprog/sc-boundary.ck
new file mode 100644
index 0000000..3f7cbaf
--- /dev/null
+++ b/src/tests/userprog/sc-boundary.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(sc-boundary) begin
+sc-boundary: exit(42)
+EOF
+pass;
diff --git a/src/tests/userprog/wait-bad-pid.c b/src/tests/userprog/wait-bad-pid.c
new file mode 100644
index 0000000..3fe8ee4
--- /dev/null
+++ b/src/tests/userprog/wait-bad-pid.c
@@ -0,0 +1,11 @@
+/* Waits for an invalid pid.  This may fail or terminate the
+   process with -1 exit code. */
+
+#include <syscall.h>
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  wait ((pid_t) 0x0c020301);
+}
diff --git a/src/tests/userprog/wait-bad-pid.ck b/src/tests/userprog/wait-bad-pid.ck
new file mode 100644
index 0000000..db63fb9
--- /dev/null
+++ b/src/tests/userprog/wait-bad-pid.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(wait-bad-pid) begin
+(wait-bad-pid) end
+wait-bad-pid: exit(0)
+EOF
+(wait-bad-pid) begin
+wait-bad-pid: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/wait-killed.c b/src/tests/userprog/wait-killed.c
new file mode 100644
index 0000000..6a2a6b5
--- /dev/null
+++ b/src/tests/userprog/wait-killed.c
@@ -0,0 +1,11 @@
+/* Wait for a process that will be killed for bad behavior. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("wait(exec()) = %d", wait (exec ("child-bad")));
+}
diff --git a/src/tests/userprog/wait-killed.ck b/src/tests/userprog/wait-killed.ck
new file mode 100644
index 0000000..5df0e9c
--- /dev/null
+++ b/src/tests/userprog/wait-killed.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(wait-killed) begin
+(child-bad) begin
+child-bad: exit(-1)
+(wait-killed) wait(exec()) = -1
+(wait-killed) end
+wait-killed: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/wait-simple.c b/src/tests/userprog/wait-simple.c
new file mode 100644
index 0000000..d3afcf3
--- /dev/null
+++ b/src/tests/userprog/wait-simple.c
@@ -0,0 +1,11 @@
+/* Wait for a subprocess to finish. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  msg ("wait(exec()) = %d", wait (exec ("child-simple")));
+}
diff --git a/src/tests/userprog/wait-simple.ck b/src/tests/userprog/wait-simple.ck
new file mode 100644
index 0000000..93dd577
--- /dev/null
+++ b/src/tests/userprog/wait-simple.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(wait-simple) begin
+(child-simple) run
+child-simple: exit(81)
+(wait-simple) wait(exec()) = 81
+(wait-simple) end
+wait-simple: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/wait-twice.c b/src/tests/userprog/wait-twice.c
new file mode 100644
index 0000000..785e684
--- /dev/null
+++ b/src/tests/userprog/wait-twice.c
@@ -0,0 +1,15 @@
+/* Wait for a subprocess to finish, twice.
+   The first call must wait in the usual way and return the exit code.
+   The second wait call must return -1 immediately. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  pid_t child = exec ("child-simple");
+  msg ("wait(exec()) = %d", wait (child));
+  msg ("wait(exec()) = %d", wait (child));
+}
diff --git a/src/tests/userprog/wait-twice.ck b/src/tests/userprog/wait-twice.ck
new file mode 100644
index 0000000..6d53843
--- /dev/null
+++ b/src/tests/userprog/wait-twice.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(wait-twice) begin
+(child-simple) run
+child-simple: exit(81)
+(wait-twice) wait(exec()) = 81
+(wait-twice) wait(exec()) = -1
+(wait-twice) end
+wait-twice: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/write-bad-fd.c b/src/tests/userprog/write-bad-fd.c
new file mode 100644
index 0000000..f3b1151
--- /dev/null
+++ b/src/tests/userprog/write-bad-fd.c
@@ -0,0 +1,20 @@
+/* Tries to write to an invalid fd,
+   which must either fail silently or terminate the process with
+   exit code -1. */
+
+#include <limits.h>
+#include <syscall.h>
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  char buf = 123;
+  write (0x01012342, &buf, 1);
+  write (7, &buf, 1);
+  write (2546, &buf, 1);
+  write (-5, &buf, 1);
+  write (-8192, &buf, 1);
+  write (INT_MIN + 1, &buf, 1);
+  write (INT_MAX - 1, &buf, 1);
+}
diff --git a/src/tests/userprog/write-bad-fd.ck b/src/tests/userprog/write-bad-fd.ck
new file mode 100644
index 0000000..8da7a8b
--- /dev/null
+++ b/src/tests/userprog/write-bad-fd.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(write-bad-fd) begin
+(write-bad-fd) end
+write-bad-fd: exit(0)
+EOF
+(write-bad-fd) begin
+write-bad-fd: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/write-bad-ptr.c b/src/tests/userprog/write-bad-ptr.c
new file mode 100644
index 0000000..5336479
--- /dev/null
+++ b/src/tests/userprog/write-bad-ptr.c
@@ -0,0 +1,16 @@
+/* Passes an invalid pointer to the write system call.
+   The process must be terminated with -1 exit code. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle;
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+
+  write (handle, (char *) 0x10123420, 123);
+  fail ("should have exited with -1");
+}
diff --git a/src/tests/userprog/write-bad-ptr.ck b/src/tests/userprog/write-bad-ptr.ck
new file mode 100644
index 0000000..ad9f399
--- /dev/null
+++ b/src/tests/userprog/write-bad-ptr.ck
@@ -0,0 +1,15 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(write-bad-ptr) begin
+(write-bad-ptr) open "sample.txt"
+(write-bad-ptr) end
+write-bad-ptr: exit(0)
+EOF
+(write-bad-ptr) begin
+(write-bad-ptr) open "sample.txt"
+write-bad-ptr: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/write-boundary.c b/src/tests/userprog/write-boundary.c
new file mode 100644
index 0000000..d2de1d4
--- /dev/null
+++ b/src/tests/userprog/write-boundary.c
@@ -0,0 +1,25 @@
+/* Writes data spanning two pages in virtual address space,
+   which must succeed. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/userprog/boundary.h"
+#include "tests/userprog/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle;
+  int byte_cnt;
+  char *sample_p;
+
+  sample_p = copy_string_across_boundary (sample);
+
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+
+  byte_cnt = write (handle, sample_p, sizeof sample - 1);
+  if (byte_cnt != sizeof sample - 1)
+    fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1);
+}
diff --git a/src/tests/userprog/write-boundary.ck b/src/tests/userprog/write-boundary.ck
new file mode 100644
index 0000000..7883781
--- /dev/null
+++ b/src/tests/userprog/write-boundary.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(write-boundary) begin
+(write-boundary) open "sample.txt"
+(write-boundary) end
+write-boundary: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/write-normal.c b/src/tests/userprog/write-normal.c
new file mode 100644
index 0000000..e0297aa
--- /dev/null
+++ b/src/tests/userprog/write-normal.c
@@ -0,0 +1,20 @@
+/* Try writing a file in the most normal way. */
+
+#include <syscall.h>
+#include "tests/userprog/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle, byte_cnt;
+
+  CHECK (create ("test.txt", sizeof sample - 1), "create \"test.txt\"");
+  CHECK ((handle = open ("test.txt")) > 1, "open \"test.txt\"");
+
+  byte_cnt = write (handle, sample, sizeof sample - 1);
+  if (byte_cnt != sizeof sample - 1)
+    fail ("write() returned %d instead of %zu", byte_cnt, sizeof sample - 1);
+}
+
diff --git a/src/tests/userprog/write-normal.ck b/src/tests/userprog/write-normal.ck
new file mode 100644
index 0000000..9fa6024
--- /dev/null
+++ b/src/tests/userprog/write-normal.ck
@@ -0,0 +1,12 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(write-normal) begin
+(write-normal) create "test.txt"
+(write-normal) open "test.txt"
+(write-normal) end
+write-normal: exit(0)
+EOF
+pass;
diff --git a/src/tests/userprog/write-stdin.c b/src/tests/userprog/write-stdin.c
new file mode 100644
index 0000000..491ea53
--- /dev/null
+++ b/src/tests/userprog/write-stdin.c
@@ -0,0 +1,14 @@
+/* Try writing to fd 0 (stdin), 
+   which may just fail or terminate the process with -1 exit
+   code. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  char buf = 123;
+  write (0, &buf, 1);
+}
diff --git a/src/tests/userprog/write-stdin.ck b/src/tests/userprog/write-stdin.ck
new file mode 100644
index 0000000..a6caf81
--- /dev/null
+++ b/src/tests/userprog/write-stdin.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(write-stdin) begin
+(write-stdin) end
+write-stdin: exit(0)
+EOF
+(write-stdin) begin
+write-stdin: exit(-1)
+EOF
+pass;
diff --git a/src/tests/userprog/write-zero.c b/src/tests/userprog/write-zero.c
new file mode 100644
index 0000000..d8dac9b
--- /dev/null
+++ b/src/tests/userprog/write-zero.c
@@ -0,0 +1,20 @@
+/* Try a 0-byte write, which should return 0 without writing
+   anything. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle, byte_cnt;
+  char buf;
+
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+
+  buf = 123;
+  byte_cnt = write (handle, &buf, 0);
+  if (byte_cnt != 0)
+    fail("write() returned %d instead of 0", byte_cnt);
+}
diff --git a/src/tests/userprog/write-zero.ck b/src/tests/userprog/write-zero.ck
new file mode 100644
index 0000000..cc4cd60
--- /dev/null
+++ b/src/tests/userprog/write-zero.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(write-zero) begin
+(write-zero) open "sample.txt"
+(write-zero) end
+write-zero: exit(0)
+EOF
+pass;
diff --git a/src/tests/vm/Grading b/src/tests/vm/Grading
new file mode 100644
index 0000000..f0c2c13
--- /dev/null
+++ b/src/tests/vm/Grading
@@ -0,0 +1,12 @@
+# Percentage of the testing point total designated for each set of
+# tests.
+
+# This project is primarily about virtual memory, but all the previous
+# functionality should work too, and it's easy to screw it up, thus
+# the equal weight placed on each.
+
+50%	tests/vm/Rubric.functionality
+15%	tests/vm/Rubric.robustness
+10%	tests/userprog/Rubric.functionality
+5%	tests/userprog/Rubric.robustness
+20%	tests/filesys/base/Rubric
diff --git a/src/tests/vm/Make.tests b/src/tests/vm/Make.tests
new file mode 100644
index 0000000..04b1b81
--- /dev/null
+++ b/src/tests/vm/Make.tests
@@ -0,0 +1,103 @@
+# -*- makefile -*-
+
+tests/vm_TESTS = $(addprefix tests/vm/,pt-grow-stack pt-grow-pusha	\
+pt-grow-bad pt-big-stk-obj pt-bad-addr pt-bad-read pt-write-code	\
+pt-write-code2 pt-grow-stk-sc page-linear page-parallel page-merge-seq	\
+page-merge-par page-merge-stk page-merge-mm page-shuffle mmap-read	\
+mmap-close mmap-unmap mmap-overlap mmap-twice mmap-write mmap-exit	\
+mmap-shuffle mmap-bad-fd mmap-clean mmap-inherit mmap-misalign		\
+mmap-null mmap-over-code mmap-over-data mmap-over-stk mmap-remove	\
+mmap-zero)
+
+tests/vm_PROGS = $(tests/vm_TESTS) $(addprefix tests/vm/,child-linear	\
+child-sort child-qsort child-qsort-mm child-mm-wrt child-inherit)
+
+tests/vm/pt-grow-stack_SRC = tests/vm/pt-grow-stack.c tests/arc4.c	\
+tests/cksum.c tests/lib.c tests/main.c
+tests/vm/pt-grow-pusha_SRC = tests/vm/pt-grow-pusha.c tests/lib.c	\
+tests/main.c
+tests/vm/pt-grow-bad_SRC = tests/vm/pt-grow-bad.c tests/lib.c tests/main.c
+tests/vm/pt-big-stk-obj_SRC = tests/vm/pt-big-stk-obj.c tests/arc4.c	\
+tests/cksum.c tests/lib.c tests/main.c
+tests/vm/pt-bad-addr_SRC = tests/vm/pt-bad-addr.c tests/lib.c tests/main.c
+tests/vm/pt-bad-read_SRC = tests/vm/pt-bad-read.c tests/lib.c tests/main.c
+tests/vm/pt-write-code_SRC = tests/vm/pt-write-code.c tests/lib.c tests/main.c
+tests/vm/pt-write-code2_SRC = tests/vm/pt-write-code-2.c tests/lib.c tests/main.c
+tests/vm/pt-grow-stk-sc_SRC = tests/vm/pt-grow-stk-sc.c tests/lib.c tests/main.c
+tests/vm/page-linear_SRC = tests/vm/page-linear.c tests/arc4.c	\
+tests/lib.c tests/main.c
+tests/vm/page-parallel_SRC = tests/vm/page-parallel.c tests/lib.c tests/main.c
+tests/vm/page-merge-seq_SRC = tests/vm/page-merge-seq.c tests/arc4.c	\
+tests/lib.c tests/main.c
+tests/vm/page-merge-par_SRC = tests/vm/page-merge-par.c \
+tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
+tests/vm/page-merge-stk_SRC = tests/vm/page-merge-stk.c \
+tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
+tests/vm/page-merge-mm_SRC = tests/vm/page-merge-mm.c \
+tests/vm/parallel-merge.c tests/arc4.c tests/lib.c tests/main.c
+tests/vm/page-shuffle_SRC = tests/vm/page-shuffle.c tests/arc4.c	\
+tests/cksum.c tests/lib.c tests/main.c
+tests/vm/mmap-read_SRC = tests/vm/mmap-read.c tests/lib.c tests/main.c
+tests/vm/mmap-close_SRC = tests/vm/mmap-close.c tests/lib.c tests/main.c
+tests/vm/mmap-unmap_SRC = tests/vm/mmap-unmap.c tests/lib.c tests/main.c
+tests/vm/mmap-overlap_SRC = tests/vm/mmap-overlap.c tests/lib.c tests/main.c
+tests/vm/mmap-twice_SRC = tests/vm/mmap-twice.c tests/lib.c tests/main.c
+tests/vm/mmap-write_SRC = tests/vm/mmap-write.c tests/lib.c tests/main.c
+tests/vm/mmap-exit_SRC = tests/vm/mmap-exit.c tests/lib.c tests/main.c
+tests/vm/mmap-shuffle_SRC = tests/vm/mmap-shuffle.c tests/arc4.c	\
+tests/cksum.c tests/lib.c tests/main.c
+tests/vm/mmap-bad-fd_SRC = tests/vm/mmap-bad-fd.c tests/lib.c tests/main.c
+tests/vm/mmap-clean_SRC = tests/vm/mmap-clean.c tests/lib.c tests/main.c
+tests/vm/mmap-inherit_SRC = tests/vm/mmap-inherit.c tests/lib.c tests/main.c
+tests/vm/mmap-misalign_SRC = tests/vm/mmap-misalign.c tests/lib.c	\
+tests/main.c
+tests/vm/mmap-null_SRC = tests/vm/mmap-null.c tests/lib.c tests/main.c
+tests/vm/mmap-over-code_SRC = tests/vm/mmap-over-code.c tests/lib.c	\
+tests/main.c
+tests/vm/mmap-over-data_SRC = tests/vm/mmap-over-data.c tests/lib.c	\
+tests/main.c
+tests/vm/mmap-over-stk_SRC = tests/vm/mmap-over-stk.c tests/lib.c tests/main.c
+tests/vm/mmap-remove_SRC = tests/vm/mmap-remove.c tests/lib.c tests/main.c
+tests/vm/mmap-zero_SRC = tests/vm/mmap-zero.c tests/lib.c tests/main.c
+
+tests/vm/child-linear_SRC = tests/vm/child-linear.c tests/arc4.c tests/lib.c
+tests/vm/child-qsort_SRC = tests/vm/child-qsort.c tests/vm/qsort.c tests/lib.c
+tests/vm/child-qsort-mm_SRC = tests/vm/child-qsort-mm.c tests/vm/qsort.c \
+tests/lib.c
+tests/vm/child-sort_SRC = tests/vm/child-sort.c tests/lib.c
+tests/vm/child-mm-wrt_SRC = tests/vm/child-mm-wrt.c tests/lib.c tests/main.c
+tests/vm/child-inherit_SRC = tests/vm/child-inherit.c tests/lib.c tests/main.c
+
+tests/vm/pt-bad-read_PUTFILES = tests/vm/sample.txt
+tests/vm/pt-write-code2_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-close_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-read_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-unmap_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-twice_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-overlap_PUTFILES = tests/vm/zeros
+tests/vm/mmap-exit_PUTFILES = tests/vm/child-mm-wrt
+tests/vm/page-parallel_PUTFILES = tests/vm/child-linear
+tests/vm/page-merge-seq_PUTFILES = tests/vm/child-sort
+tests/vm/page-merge-par_PUTFILES = tests/vm/child-sort
+tests/vm/page-merge-stk_PUTFILES = tests/vm/child-qsort
+tests/vm/page-merge-mm_PUTFILES = tests/vm/child-qsort-mm
+tests/vm/mmap-clean_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-inherit_PUTFILES = tests/vm/sample.txt tests/vm/child-inherit
+tests/vm/mmap-misalign_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-null_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-over-code_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-over-data_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-over-stk_PUTFILES = tests/vm/sample.txt
+tests/vm/mmap-remove_PUTFILES = tests/vm/sample.txt
+
+tests/vm/page-linear.output: TIMEOUT = 300
+tests/vm/page-shuffle.output: TIMEOUT = 600
+tests/vm/mmap-shuffle.output: TIMEOUT = 600
+tests/vm/page-merge-seq.output: TIMEOUT = 600
+tests/vm/page-merge-par.output: TIMEOUT = 600
+
+tests/vm/zeros:
+	dd if=/dev/zero of=$@ bs=1024 count=6
+
+clean::
+	rm -f tests/vm/zeros
diff --git a/src/tests/vm/Rubric.functionality b/src/tests/vm/Rubric.functionality
new file mode 100644
index 0000000..8a86612
--- /dev/null
+++ b/src/tests/vm/Rubric.functionality
@@ -0,0 +1,30 @@
+Functionality of virtual memory subsystem:
+- Test stack growth.
+3	pt-grow-stack
+3	pt-grow-stk-sc
+3	pt-big-stk-obj
+3	pt-grow-pusha
+
+- Test paging behavior.
+3	page-linear
+3	page-parallel
+3	page-shuffle
+4	page-merge-seq
+4	page-merge-par
+4	page-merge-mm
+4	page-merge-stk
+
+- Test "mmap" system call.
+2	mmap-read
+2	mmap-write
+2	mmap-shuffle
+
+2	mmap-twice
+
+2	mmap-unmap
+1	mmap-exit
+
+3	mmap-clean
+
+2	mmap-close
+2	mmap-remove
diff --git a/src/tests/vm/Rubric.robustness b/src/tests/vm/Rubric.robustness
new file mode 100644
index 0000000..0b2552f
--- /dev/null
+++ b/src/tests/vm/Rubric.robustness
@@ -0,0 +1,21 @@
+Robustness of virtual memory subsystem:
+- Test robustness of page table support.
+2	pt-bad-addr
+3	pt-bad-read
+2	pt-write-code
+3	pt-write-code2
+4	pt-grow-bad
+
+- Test robustness of "mmap" system call.
+1	mmap-bad-fd
+1	mmap-inherit
+1	mmap-null
+1	mmap-zero
+
+2	mmap-misalign
+
+2	mmap-over-code
+2	mmap-over-data
+2	mmap-over-stk
+2	mmap-overlap
+
diff --git a/src/tests/vm/child-inherit.c b/src/tests/vm/child-inherit.c
new file mode 100644
index 0000000..d3186a1
--- /dev/null
+++ b/src/tests/vm/child-inherit.c
@@ -0,0 +1,16 @@
+/* Child process for mmap-inherit test.
+   Tries to write to a mapping present in the parent.
+   The process must be terminated with -1 exit code. */
+
+#include <string.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  memset ((char *) 0x54321000, 0, 4096);
+  fail ("child can modify parent's memory mappings");
+}
+
diff --git a/src/tests/vm/child-linear.c b/src/tests/vm/child-linear.c
new file mode 100644
index 0000000..cdbe82e
--- /dev/null
+++ b/src/tests/vm/child-linear.c
@@ -0,0 +1,36 @@
+/* Child process of page-parallel.
+   Encrypts 1 MB of zeros, then decrypts it, and ensures that
+   the zeros are back. */
+
+#include <string.h>
+#include "tests/arc4.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define SIZE (1024 * 1024)
+static char buf[SIZE];
+
+int
+main (int argc, char *argv[])
+{
+  const char *key = argv[argc - 1];
+  struct arc4 arc4;
+  size_t i;
+
+  test_name = "child-linear";
+
+  /* Encrypt zeros. */
+  arc4_init (&arc4, key, strlen (key));
+  arc4_crypt (&arc4, buf, SIZE);
+
+  /* Decrypt back to zeros. */
+  arc4_init (&arc4, key, strlen (key));
+  arc4_crypt (&arc4, buf, SIZE);
+
+  /* Check that it's all zeros. */
+  for (i = 0; i < SIZE; i++)
+    if (buf[i] != '\0')
+      fail ("byte %zu != 0", i);
+
+  return 0x42;
+}
diff --git a/src/tests/vm/child-mm-wrt.c b/src/tests/vm/child-mm-wrt.c
new file mode 100644
index 0000000..8419788
--- /dev/null
+++ b/src/tests/vm/child-mm-wrt.c
@@ -0,0 +1,24 @@
+/* Child process of mmap-exit.
+   Mmaps a file and writes to it via the mmap'ing, then exits
+   without calling munmap.  The data in the mapped region must be
+   written out at program termination. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define ACTUAL ((void *) 0x10000000)
+
+void
+test_main (void)
+{
+  int handle;
+
+  CHECK (create ("sample.txt", sizeof sample), "create \"sample.txt\"");
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK (mmap (handle, ACTUAL) != MAP_FAILED, "mmap \"sample.txt\"");
+  memcpy (ACTUAL, sample, sizeof sample);
+}
+
diff --git a/src/tests/vm/child-qsort-mm.c b/src/tests/vm/child-qsort-mm.c
new file mode 100644
index 0000000..8d9549d
--- /dev/null
+++ b/src/tests/vm/child-qsort-mm.c
@@ -0,0 +1,24 @@
+/* Mmaps a 128 kB file "sorts" the bytes in it, using quick sort,
+   a multi-pass divide and conquer algorithm.  */
+
+#include <debug.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+#include "tests/vm/qsort.h"
+
+int
+main (int argc UNUSED, char *argv[]) 
+{
+  int handle;
+  unsigned char *p = (unsigned char *) 0x10000000;
+
+  test_name = "child-qsort-mm";
+  quiet = true;
+
+  CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
+  CHECK (mmap (handle, p) != MAP_FAILED, "mmap \"%s\"", argv[1]);
+  qsort_bytes (p, 1024 * 128);
+  
+  return 80;
+}
diff --git a/src/tests/vm/child-qsort.c b/src/tests/vm/child-qsort.c
new file mode 100644
index 0000000..e243704
--- /dev/null
+++ b/src/tests/vm/child-qsort.c
@@ -0,0 +1,31 @@
+/* Reads a 128 kB file onto the stack and "sorts" the bytes in
+   it, using quick sort, a multi-pass divide and conquer
+   algorithm.  The sorted data is written back to the same file
+   in-place. */
+
+#include <debug.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+#include "tests/vm/qsort.h"
+
+int
+main (int argc UNUSED, char *argv[]) 
+{
+  int handle;
+  unsigned char buf[128 * 1024];
+  size_t size;
+
+  test_name = "child-qsort";
+  quiet = true;
+
+  CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
+
+  size = read (handle, buf, sizeof buf);
+  qsort_bytes (buf, sizeof buf);
+  seek (handle, 0);
+  write (handle, buf, size);
+  close (handle);
+  
+  return 72;
+}
diff --git a/src/tests/vm/child-sort.c b/src/tests/vm/child-sort.c
new file mode 100644
index 0000000..2b13b67
--- /dev/null
+++ b/src/tests/vm/child-sort.c
@@ -0,0 +1,41 @@
+/* Reads a 128 kB file into static data and "sorts" the bytes in
+   it, using counting sort, a single-pass algorithm.  The sorted
+   data is written back to the same file in-place. */
+
+#include <debug.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+unsigned char buf[128 * 1024];
+size_t histogram[256];
+
+int
+main (int argc UNUSED, char *argv[]) 
+{
+  int handle;
+  unsigned char *p;
+  size_t size;
+  size_t i;
+
+  test_name = "child-sort";
+  quiet = true;
+
+  CHECK ((handle = open (argv[1])) > 1, "open \"%s\"", argv[1]);
+
+  size = read (handle, buf, sizeof buf);
+  for (i = 0; i < size; i++)
+    histogram[buf[i]]++;
+  p = buf;
+  for (i = 0; i < sizeof histogram / sizeof *histogram; i++) 
+    {
+      size_t j = histogram[i];
+      while (j-- > 0)
+        *p++ = i;
+    }
+  seek (handle, 0);
+  write (handle, buf, size);
+  close (handle);
+  
+  return 123;
+}
diff --git a/src/tests/vm/mmap-bad-fd.c b/src/tests/vm/mmap-bad-fd.c
new file mode 100644
index 0000000..76a7b50
--- /dev/null
+++ b/src/tests/vm/mmap-bad-fd.c
@@ -0,0 +1,15 @@
+/* Tries to mmap an invalid fd,
+   which must either fail silently or terminate the process with
+   exit code -1. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  CHECK (mmap (0x5678, (void *) 0x10000000) == MAP_FAILED,
+         "try to mmap invalid fd");
+}
+
diff --git a/src/tests/vm/mmap-bad-fd.ck b/src/tests/vm/mmap-bad-fd.ck
new file mode 100644
index 0000000..f3f58d5
--- /dev/null
+++ b/src/tests/vm/mmap-bad-fd.ck
@@ -0,0 +1,15 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF', <<'EOF']);
+(mmap-bad-fd) begin
+(mmap-bad-fd) try to mmap invalid fd
+(mmap-bad-fd) end
+mmap-bad-fd: exit(0)
+EOF
+(mmap-bad-fd) begin
+(mmap-bad-fd) try to mmap invalid fd
+mmap-bad-fd: exit(-1)
+EOF
+pass;
diff --git a/src/tests/vm/mmap-clean.c b/src/tests/vm/mmap-clean.c
new file mode 100644
index 0000000..ea1dc9c
--- /dev/null
+++ b/src/tests/vm/mmap-clean.c
@@ -0,0 +1,53 @@
+/* Verifies that mmap'd regions are only written back on munmap
+   if the data was actually modified in memory. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  static const char overwrite[] = "Now is the time for all good...";
+  static char buffer[sizeof sample - 1];
+  char *actual = (char *) 0x54321000;
+  int handle;
+  mapid_t map;
+
+  /* Open file, map, verify data. */
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\"");
+  if (memcmp (actual, sample, strlen (sample)))
+    fail ("read of mmap'd file reported bad data");
+
+  /* Modify file. */
+  CHECK (write (handle, overwrite, strlen (overwrite))
+         == (int) strlen (overwrite),
+         "write \"sample.txt\"");
+
+  /* Close mapping.  Data should not be written back, because we
+     didn't modify it via the mapping. */
+  msg ("munmap \"sample.txt\"");
+  munmap (map);
+
+  /* Read file back. */
+  msg ("seek \"sample.txt\"");
+  seek (handle, 0);
+  CHECK (read (handle, buffer, sizeof buffer) == sizeof buffer,
+         "read \"sample.txt\"");
+
+  /* Verify that file overwrite worked. */
+  if (memcmp (buffer, overwrite, strlen (overwrite))
+      || memcmp (buffer + strlen (overwrite), sample + strlen (overwrite),
+                 strlen (sample) - strlen (overwrite))) 
+    {
+      if (!memcmp (buffer, sample, strlen (sample)))
+        fail ("munmap wrote back clean page");
+      else
+        fail ("read surprising data from file"); 
+    }
+  else
+    msg ("file change was retained after munmap");
+}
diff --git a/src/tests/vm/mmap-clean.ck b/src/tests/vm/mmap-clean.ck
new file mode 100644
index 0000000..1666d6c
--- /dev/null
+++ b/src/tests/vm/mmap-clean.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-clean) begin
+(mmap-clean) open "sample.txt"
+(mmap-clean) mmap "sample.txt"
+(mmap-clean) write "sample.txt"
+(mmap-clean) munmap "sample.txt"
+(mmap-clean) seek "sample.txt"
+(mmap-clean) read "sample.txt"
+(mmap-clean) file change was retained after munmap
+(mmap-clean) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-close.c b/src/tests/vm/mmap-close.c
new file mode 100644
index 0000000..d016ee3
--- /dev/null
+++ b/src/tests/vm/mmap-close.c
@@ -0,0 +1,27 @@
+/* Verifies that memory mappings persist after file close. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/arc4.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define ACTUAL ((void *) 0x10000000)
+
+void
+test_main (void)
+{
+  int handle;
+  mapid_t map;
+
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\"");
+
+  close (handle);
+
+  if (memcmp (ACTUAL, sample, strlen (sample)))
+    fail ("read of mmap'd file reported bad data");
+
+  munmap (map);
+}
diff --git a/src/tests/vm/mmap-close.ck b/src/tests/vm/mmap-close.ck
new file mode 100644
index 0000000..d15e41a
--- /dev/null
+++ b/src/tests/vm/mmap-close.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-close) begin
+(mmap-close) open "sample.txt"
+(mmap-close) mmap "sample.txt"
+(mmap-close) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-exit.c b/src/tests/vm/mmap-exit.c
new file mode 100644
index 0000000..7a2278a
--- /dev/null
+++ b/src/tests/vm/mmap-exit.c
@@ -0,0 +1,22 @@
+/* Executes child-mm-wrt and verifies that the writes that should
+   have occurred really did. */
+
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  pid_t child;
+
+  /* Make child write file. */
+  quiet = true;
+  CHECK ((child = exec ("child-mm-wrt")) != -1, "exec \"child-mm-wrt\"");
+  CHECK (wait (child) == 0, "wait for child (should return 0)");
+  quiet = false;
+
+  /* Check file contents. */
+  check_file ("sample.txt", sample, sizeof sample);
+}
diff --git a/src/tests/vm/mmap-exit.ck b/src/tests/vm/mmap-exit.ck
new file mode 100644
index 0000000..457d34a
--- /dev/null
+++ b/src/tests/vm/mmap-exit.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-exit) begin
+(child-mm-wrt) begin
+(child-mm-wrt) create "sample.txt"
+(child-mm-wrt) open "sample.txt"
+(child-mm-wrt) mmap "sample.txt"
+(child-mm-wrt) end
+(mmap-exit) open "sample.txt" for verification
+(mmap-exit) verified contents of "sample.txt"
+(mmap-exit) close "sample.txt"
+(mmap-exit) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-inherit.c b/src/tests/vm/mmap-inherit.c
new file mode 100644
index 0000000..7fa9607
--- /dev/null
+++ b/src/tests/vm/mmap-inherit.c
@@ -0,0 +1,32 @@
+/* Maps a file into memory and runs child-inherit to verify that
+   mappings are not inherited. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  char *actual = (char *) 0x54321000;
+  int handle;
+  pid_t child;
+
+  /* Open file, map, verify data. */
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK (mmap (handle, actual) != MAP_FAILED, "mmap \"sample.txt\"");
+  if (memcmp (actual, sample, strlen (sample)))
+    fail ("read of mmap'd file reported bad data");
+
+  /* Spawn child and wait. */
+  CHECK ((child = exec ("child-inherit")) != -1, "exec \"child-inherit\"");
+  quiet = true;
+  CHECK (wait (child) == -1, "wait for child (should return -1)");
+  quiet = false;
+
+  /* Verify data again. */
+  CHECK (!memcmp (actual, sample, strlen (sample)),
+         "checking that mmap'd file still has same data");
+}
diff --git a/src/tests/vm/mmap-inherit.ck b/src/tests/vm/mmap-inherit.ck
new file mode 100644
index 0000000..c54638a
--- /dev/null
+++ b/src/tests/vm/mmap-inherit.ck
@@ -0,0 +1,16 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
+(mmap-inherit) begin
+(mmap-inherit) open "sample.txt"
+(mmap-inherit) mmap "sample.txt"
+(mmap-inherit) exec "child-inherit"
+(child-inherit) begin
+child-inherit: exit(-1)
+(mmap-inherit) checking that mmap'd file still has same data
+(mmap-inherit) end
+mmap-inherit: exit(0)
+EOF
+pass;
diff --git a/src/tests/vm/mmap-misalign.c b/src/tests/vm/mmap-misalign.c
new file mode 100644
index 0000000..34141a9
--- /dev/null
+++ b/src/tests/vm/mmap-misalign.c
@@ -0,0 +1,16 @@
+/* Verifies that misaligned memory mappings are disallowed. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle;
+  
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK (mmap (handle, (void *) 0x10001234) == MAP_FAILED,
+         "try to mmap at misaligned address");
+}
+
diff --git a/src/tests/vm/mmap-misalign.ck b/src/tests/vm/mmap-misalign.ck
new file mode 100644
index 0000000..145a2e8
--- /dev/null
+++ b/src/tests/vm/mmap-misalign.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-misalign) begin
+(mmap-misalign) open "sample.txt"
+(mmap-misalign) try to mmap at misaligned address
+(mmap-misalign) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-null.c b/src/tests/vm/mmap-null.c
new file mode 100644
index 0000000..f8ef075
--- /dev/null
+++ b/src/tests/vm/mmap-null.c
@@ -0,0 +1,15 @@
+/* Verifies that memory mappings at address 0 are disallowed. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle;
+  
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK (mmap (handle, NULL) == MAP_FAILED, "try to mmap at address 0");
+}
+
diff --git a/src/tests/vm/mmap-null.ck b/src/tests/vm/mmap-null.ck
new file mode 100644
index 0000000..aacdd65
--- /dev/null
+++ b/src/tests/vm/mmap-null.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-null) begin
+(mmap-null) open "sample.txt"
+(mmap-null) try to mmap at address 0
+(mmap-null) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-over-code.c b/src/tests/vm/mmap-over-code.c
new file mode 100644
index 0000000..d3619a3
--- /dev/null
+++ b/src/tests/vm/mmap-over-code.c
@@ -0,0 +1,19 @@
+/* Verifies that mapping over the code segment is disallowed. */
+
+#include <stdint.h>
+#include <round.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  uintptr_t test_main_page = ROUND_DOWN ((uintptr_t) test_main, 4096);
+  int handle;
+  
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK (mmap (handle, (void *) test_main_page) == MAP_FAILED,
+         "try to mmap over code segment");
+}
+
diff --git a/src/tests/vm/mmap-over-code.ck b/src/tests/vm/mmap-over-code.ck
new file mode 100644
index 0000000..b5b23c7
--- /dev/null
+++ b/src/tests/vm/mmap-over-code.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-over-code) begin
+(mmap-over-code) open "sample.txt"
+(mmap-over-code) try to mmap over code segment
+(mmap-over-code) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-over-data.c b/src/tests/vm/mmap-over-data.c
new file mode 100644
index 0000000..9ea5d49
--- /dev/null
+++ b/src/tests/vm/mmap-over-data.c
@@ -0,0 +1,21 @@
+/* Verifies that mapping over the data segment is disallowed. */
+
+#include <stdint.h>
+#include <round.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+static char x;
+
+void
+test_main (void) 
+{
+  uintptr_t x_page = ROUND_DOWN ((uintptr_t) &x, 4096);
+  int handle;
+  
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK (mmap (handle, (void *) x_page) == MAP_FAILED,
+         "try to mmap over data segment");
+}
+
diff --git a/src/tests/vm/mmap-over-data.ck b/src/tests/vm/mmap-over-data.ck
new file mode 100644
index 0000000..98770cc
--- /dev/null
+++ b/src/tests/vm/mmap-over-data.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-over-data) begin
+(mmap-over-data) open "sample.txt"
+(mmap-over-data) try to mmap over data segment
+(mmap-over-data) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-over-stk.c b/src/tests/vm/mmap-over-stk.c
new file mode 100644
index 0000000..4e241e8
--- /dev/null
+++ b/src/tests/vm/mmap-over-stk.c
@@ -0,0 +1,19 @@
+/* Verifies that mapping over the stack segment is disallowed. */
+
+#include <stdint.h>
+#include <round.h>
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  int handle;
+  uintptr_t handle_page = ROUND_DOWN ((uintptr_t) &handle, 4096);
+  
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK (mmap (handle, (void *) handle_page) == MAP_FAILED,
+         "try to mmap over stack segment");
+}
+
diff --git a/src/tests/vm/mmap-over-stk.ck b/src/tests/vm/mmap-over-stk.ck
new file mode 100644
index 0000000..e6880cf
--- /dev/null
+++ b/src/tests/vm/mmap-over-stk.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-over-stk) begin
+(mmap-over-stk) open "sample.txt"
+(mmap-over-stk) try to mmap over stack segment
+(mmap-over-stk) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-overlap.c b/src/tests/vm/mmap-overlap.c
new file mode 100644
index 0000000..668ae5f
--- /dev/null
+++ b/src/tests/vm/mmap-overlap.c
@@ -0,0 +1,20 @@
+/* Verifies that overlapping memory mappings are disallowed. */
+
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  char *start = (char *) 0x10000000;
+  int fd[2];
+
+  CHECK ((fd[0] = open ("zeros")) > 1, "open \"zeros\" once");
+  CHECK (mmap (fd[0], start) != MAP_FAILED, "mmap \"zeros\"");
+  CHECK ((fd[1] = open ("zeros")) > 1 && fd[0] != fd[1],
+         "open \"zeros\" again");
+  CHECK (mmap (fd[1], start + 4096) == MAP_FAILED,
+         "try to mmap \"zeros\" again");
+}
diff --git a/src/tests/vm/mmap-overlap.ck b/src/tests/vm/mmap-overlap.ck
new file mode 100644
index 0000000..f13801e
--- /dev/null
+++ b/src/tests/vm/mmap-overlap.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-overlap) begin
+(mmap-overlap) open "zeros" once
+(mmap-overlap) mmap "zeros"
+(mmap-overlap) open "zeros" again
+(mmap-overlap) try to mmap "zeros" again
+(mmap-overlap) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-read.c b/src/tests/vm/mmap-read.c
new file mode 100644
index 0000000..c0f23a1
--- /dev/null
+++ b/src/tests/vm/mmap-read.c
@@ -0,0 +1,32 @@
+/* Uses a memory mapping to read a file. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  char *actual = (char *) 0x10000000;
+  int handle;
+  mapid_t map;
+  size_t i;
+
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\"");
+
+  /* Check that data is correct. */
+  if (memcmp (actual, sample, strlen (sample)))
+    fail ("read of mmap'd file reported bad data");
+
+  /* Verify that data is followed by zeros. */
+  for (i = strlen (sample); i < 4096; i++)
+    if (actual[i] != 0)
+      fail ("byte %zu of mmap'd region has value %02hhx (should be 0)",
+            i, actual[i]);
+
+  munmap (map);
+  close (handle);
+}
diff --git a/src/tests/vm/mmap-read.ck b/src/tests/vm/mmap-read.ck
new file mode 100644
index 0000000..95ab790
--- /dev/null
+++ b/src/tests/vm/mmap-read.ck
@@ -0,0 +1,11 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-read) begin
+(mmap-read) open "sample.txt"
+(mmap-read) mmap "sample.txt"
+(mmap-read) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-remove.c b/src/tests/vm/mmap-remove.c
new file mode 100644
index 0000000..5f7444d
--- /dev/null
+++ b/src/tests/vm/mmap-remove.c
@@ -0,0 +1,43 @@
+/* Deletes and closes file that is mapped into memory
+   and verifies that it can still be read through the mapping. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  char *actual = (char *) 0x10000000;
+  int handle;
+  mapid_t map;
+  size_t i;
+
+  /* Map file. */
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK ((map = mmap (handle, actual)) != MAP_FAILED, "mmap \"sample.txt\"");
+
+  /* Close file and delete it. */
+  close (handle);
+  CHECK (remove ("sample.txt"), "remove \"sample.txt\"");
+  CHECK (open ("sample.txt") == -1, "try to open \"sample.txt\"");
+
+  /* Create a new file in hopes of overwriting data from the old
+     one, in case the file system has incorrectly freed the
+     file's data. */
+  CHECK (create ("another", 4096 * 10), "create \"another\"");
+
+  /* Check that mapped data is correct. */
+  if (memcmp (actual, sample, strlen (sample)))
+    fail ("read of mmap'd file reported bad data");
+
+  /* Verify that data is followed by zeros. */
+  for (i = strlen (sample); i < 4096; i++)
+    if (actual[i] != 0)
+      fail ("byte %zu of mmap'd region has value %02hhx (should be 0)",
+            i, actual[i]);
+
+  munmap (map);
+}
diff --git a/src/tests/vm/mmap-remove.ck b/src/tests/vm/mmap-remove.ck
new file mode 100644
index 0000000..d3cc938
--- /dev/null
+++ b/src/tests/vm/mmap-remove.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-remove) begin
+(mmap-remove) open "sample.txt"
+(mmap-remove) mmap "sample.txt"
+(mmap-remove) remove "sample.txt"
+(mmap-remove) try to open "sample.txt"
+(mmap-remove) create "another"
+(mmap-remove) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-shuffle.c b/src/tests/vm/mmap-shuffle.c
new file mode 100644
index 0000000..29921ad
--- /dev/null
+++ b/src/tests/vm/mmap-shuffle.c
@@ -0,0 +1,38 @@
+/* Creates a 128 kB file and repeatedly shuffles data in it
+   through a memory mapping. */
+
+#include <stdio.h>
+#include <string.h>
+#include <syscall.h>
+#include "tests/arc4.h"
+#include "tests/cksum.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define SIZE (128 * 1024)
+
+static char *buf = (char *) 0x10000000;
+
+void
+test_main (void)
+{
+  size_t i;
+  int handle;
+
+  /* Create file, mmap. */
+  CHECK (create ("buffer", SIZE), "create \"buffer\"");
+  CHECK ((handle = open ("buffer")) > 1, "open \"buffer\"");
+  CHECK (mmap (handle, buf) != MAP_FAILED, "mmap \"buffer\"");
+
+  /* Initialize. */
+  for (i = 0; i < SIZE; i++)
+    buf[i] = i * 257;
+  msg ("init: cksum=%lu", cksum (buf, SIZE));
+    
+  /* Shuffle repeatedly. */
+  for (i = 0; i < 10; i++)
+    {
+      shuffle (buf, SIZE, 1);
+      msg ("shuffle %zu: cksum=%lu", i, cksum (buf, SIZE));
+    }
+}
diff --git a/src/tests/vm/mmap-shuffle.ck b/src/tests/vm/mmap-shuffle.ck
new file mode 100644
index 0000000..c158301
--- /dev/null
+++ b/src/tests/vm/mmap-shuffle.ck
@@ -0,0 +1,47 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::cksum;
+use tests::lib;
+
+my ($init, @shuffle);
+if (1) {
+    # Use precalculated values.
+    $init = 3115322833;
+    @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467,
+		3022003332, 3614934266, 2704001777, 735775156, 1864109763);
+} else {
+    # Recalculate values.
+    my ($buf) = "";
+    for my $i (0...128 * 1024 - 1) {
+	$buf .= chr (($i * 257) & 0xff);
+    }
+    $init = cksum ($buf);
+
+    random_init (0);
+    for my $i (1...10) {
+	$buf = shuffle ($buf, length ($buf), 1);
+	push (@shuffle, cksum ($buf));
+    }
+}
+
+check_expected (IGNORE_EXIT_CODES => 1, [<<EOF]);
+(mmap-shuffle) begin
+(mmap-shuffle) create "buffer"
+(mmap-shuffle) open "buffer"
+(mmap-shuffle) mmap "buffer"
+(mmap-shuffle) init: cksum=$init
+(mmap-shuffle) shuffle 0: cksum=$shuffle[0]
+(mmap-shuffle) shuffle 1: cksum=$shuffle[1]
+(mmap-shuffle) shuffle 2: cksum=$shuffle[2]
+(mmap-shuffle) shuffle 3: cksum=$shuffle[3]
+(mmap-shuffle) shuffle 4: cksum=$shuffle[4]
+(mmap-shuffle) shuffle 5: cksum=$shuffle[5]
+(mmap-shuffle) shuffle 6: cksum=$shuffle[6]
+(mmap-shuffle) shuffle 7: cksum=$shuffle[7]
+(mmap-shuffle) shuffle 8: cksum=$shuffle[8]
+(mmap-shuffle) shuffle 9: cksum=$shuffle[9]
+(mmap-shuffle) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-twice.c b/src/tests/vm/mmap-twice.c
new file mode 100644
index 0000000..d277a37
--- /dev/null
+++ b/src/tests/vm/mmap-twice.c
@@ -0,0 +1,28 @@
+/* Maps the same file into memory twice and verifies that the
+   same data is readable in both. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  char *actual[2] = {(char *) 0x10000000, (char *) 0x20000000};
+  size_t i;
+  int handle[2];
+
+  for (i = 0; i < 2; i++) 
+    {
+      CHECK ((handle[i] = open ("sample.txt")) > 1,
+             "open \"sample.txt\" #%zu", i);
+      CHECK (mmap (handle[i], actual[i]) != MAP_FAILED,
+             "mmap \"sample.txt\" #%zu at %p", i, (void *) actual[i]);
+    }
+
+  for (i = 0; i < 2; i++)
+    CHECK (!memcmp (actual[i], sample, strlen (sample)),
+           "compare mmap'd file %zu against data", i);
+}
diff --git a/src/tests/vm/mmap-twice.ck b/src/tests/vm/mmap-twice.ck
new file mode 100644
index 0000000..05e9724
--- /dev/null
+++ b/src/tests/vm/mmap-twice.ck
@@ -0,0 +1,15 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-twice) begin
+(mmap-twice) open "sample.txt" #0
+(mmap-twice) mmap "sample.txt" #0 at 0x10000000
+(mmap-twice) open "sample.txt" #1
+(mmap-twice) mmap "sample.txt" #1 at 0x20000000
+(mmap-twice) compare mmap'd file 0 against data
+(mmap-twice) compare mmap'd file 1 against data
+(mmap-twice) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-unmap.c b/src/tests/vm/mmap-unmap.c
new file mode 100644
index 0000000..d35a79e
--- /dev/null
+++ b/src/tests/vm/mmap-unmap.c
@@ -0,0 +1,23 @@
+/* Maps and unmaps a file and verifies that the mapped region is
+   inaccessible afterward. */
+
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define ACTUAL ((void *) 0x10000000)
+
+void
+test_main (void)
+{
+  int handle;
+  mapid_t map;
+
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\"");
+
+  munmap (map);
+
+  fail ("unmapped memory is readable (%d)", *(int *) ACTUAL);
+}
diff --git a/src/tests/vm/mmap-unmap.ck b/src/tests/vm/mmap-unmap.ck
new file mode 100644
index 0000000..119658c
--- /dev/null
+++ b/src/tests/vm/mmap-unmap.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::vm::process_death;
+
+check_process_death ('mmap-unmap');
diff --git a/src/tests/vm/mmap-write.c b/src/tests/vm/mmap-write.c
new file mode 100644
index 0000000..46e8043
--- /dev/null
+++ b/src/tests/vm/mmap-write.c
@@ -0,0 +1,32 @@
+/* Writes to a file through a mapping, and unmaps the file,
+   then reads the data in the file back using the read system
+   call to verify. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define ACTUAL ((void *) 0x10000000)
+
+void
+test_main (void)
+{
+  int handle;
+  mapid_t map;
+  char buf[1024];
+
+  /* Write file via mmap. */
+  CHECK (create ("sample.txt", strlen (sample)), "create \"sample.txt\"");
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK ((map = mmap (handle, ACTUAL)) != MAP_FAILED, "mmap \"sample.txt\"");
+  memcpy (ACTUAL, sample, strlen (sample));
+  munmap (map);
+
+  /* Read back via read(). */
+  read (handle, buf, strlen (sample));
+  CHECK (!memcmp (buf, sample, strlen (sample)),
+         "compare read data against written data");
+  close (handle);
+}
diff --git a/src/tests/vm/mmap-write.ck b/src/tests/vm/mmap-write.ck
new file mode 100644
index 0000000..d2c9cc5
--- /dev/null
+++ b/src/tests/vm/mmap-write.ck
@@ -0,0 +1,13 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(mmap-write) begin
+(mmap-write) create "sample.txt"
+(mmap-write) open "sample.txt"
+(mmap-write) mmap "sample.txt"
+(mmap-write) compare read data against written data
+(mmap-write) end
+EOF
+pass;
diff --git a/src/tests/vm/mmap-zero.c b/src/tests/vm/mmap-zero.c
new file mode 100644
index 0000000..368b759
--- /dev/null
+++ b/src/tests/vm/mmap-zero.c
@@ -0,0 +1,27 @@
+/* Tries to map a zero-length file, which may or may not work but
+   should not terminate the process or crash.
+   Then dereferences the address that we tried to map,
+   and the process must be terminated with -1 exit code. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void) 
+{
+  char *data = (char *) 0x7f000000;
+  int handle;
+
+  CHECK (create ("empty", 0), "create empty file \"empty\"");
+  CHECK ((handle = open ("empty")) > 1, "open \"empty\"");
+
+  /* Calling mmap() might succeed or fail.  We don't care. */
+  msg ("mmap \"empty\"");
+  mmap (handle, data);
+
+  /* Regardless of whether the call worked, *data should cause
+     the process to be terminated. */
+  fail ("unmapped memory is readable (%d)", *data);
+}
+
diff --git a/src/tests/vm/mmap-zero.ck b/src/tests/vm/mmap-zero.ck
new file mode 100644
index 0000000..6033d5d
--- /dev/null
+++ b/src/tests/vm/mmap-zero.ck
@@ -0,0 +1,12 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
+(mmap-zero) begin
+(mmap-zero) create empty file "empty"
+(mmap-zero) open "empty"
+(mmap-zero) mmap "empty"
+mmap-zero: exit(-1)
+EOF
+pass;
diff --git a/src/tests/vm/page-linear.c b/src/tests/vm/page-linear.c
new file mode 100644
index 0000000..652a47b
--- /dev/null
+++ b/src/tests/vm/page-linear.c
@@ -0,0 +1,44 @@
+/* Encrypts, then decrypts, 2 MB of memory and verifies that the
+   values are as they should be. */
+
+#include <string.h>
+#include "tests/arc4.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define SIZE (2 * 1024 * 1024)
+
+static char buf[SIZE];
+
+void
+test_main (void)
+{
+  struct arc4 arc4;
+  size_t i;
+
+  /* Initialize to 0x5a. */
+  msg ("initialize");
+  memset (buf, 0x5a, sizeof buf);
+
+  /* Check that it's all 0x5a. */
+  msg ("read pass");
+  for (i = 0; i < SIZE; i++)
+    if (buf[i] != 0x5a)
+      fail ("byte %zu != 0x5a", i);
+
+  /* Encrypt zeros. */
+  msg ("read/modify/write pass one");
+  arc4_init (&arc4, "foobar", 6);
+  arc4_crypt (&arc4, buf, SIZE);
+
+  /* Decrypt back to zeros. */
+  msg ("read/modify/write pass two");
+  arc4_init (&arc4, "foobar", 6);
+  arc4_crypt (&arc4, buf, SIZE);
+
+  /* Check that it's all 0x5a. */
+  msg ("read pass");
+  for (i = 0; i < SIZE; i++)
+    if (buf[i] != 0x5a)
+      fail ("byte %zu != 0x5a", i);
+}
diff --git a/src/tests/vm/page-linear.ck b/src/tests/vm/page-linear.ck
new file mode 100644
index 0000000..dcbc884
--- /dev/null
+++ b/src/tests/vm/page-linear.ck
@@ -0,0 +1,14 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(page-linear) begin
+(page-linear) initialize
+(page-linear) read pass
+(page-linear) read/modify/write pass one
+(page-linear) read/modify/write pass two
+(page-linear) read pass
+(page-linear) end
+EOF
+pass;
diff --git a/src/tests/vm/page-merge-mm.c b/src/tests/vm/page-merge-mm.c
new file mode 100644
index 0000000..908c71c
--- /dev/null
+++ b/src/tests/vm/page-merge-mm.c
@@ -0,0 +1,8 @@
+#include "tests/main.h"
+#include "tests/vm/parallel-merge.h"
+
+void
+test_main (void) 
+{
+  parallel_merge ("child-qsort-mm", 80);
+}
diff --git a/src/tests/vm/page-merge-mm.ck b/src/tests/vm/page-merge-mm.ck
new file mode 100644
index 0000000..74fa980
--- /dev/null
+++ b/src/tests/vm/page-merge-mm.ck
@@ -0,0 +1,29 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(page-merge-mm) begin
+(page-merge-mm) init
+(page-merge-mm) sort chunk 0
+(page-merge-mm) sort chunk 1
+(page-merge-mm) sort chunk 2
+(page-merge-mm) sort chunk 3
+(page-merge-mm) sort chunk 4
+(page-merge-mm) sort chunk 5
+(page-merge-mm) sort chunk 6
+(page-merge-mm) sort chunk 7
+(page-merge-mm) wait for child 0
+(page-merge-mm) wait for child 1
+(page-merge-mm) wait for child 2
+(page-merge-mm) wait for child 3
+(page-merge-mm) wait for child 4
+(page-merge-mm) wait for child 5
+(page-merge-mm) wait for child 6
+(page-merge-mm) wait for child 7
+(page-merge-mm) merge
+(page-merge-mm) verify
+(page-merge-mm) success, buf_idx=1,048,576
+(page-merge-mm) end
+EOF
+pass;
diff --git a/src/tests/vm/page-merge-par.c b/src/tests/vm/page-merge-par.c
new file mode 100644
index 0000000..e7e1609
--- /dev/null
+++ b/src/tests/vm/page-merge-par.c
@@ -0,0 +1,8 @@
+#include "tests/main.h"
+#include "tests/vm/parallel-merge.h"
+
+void
+test_main (void) 
+{
+  parallel_merge ("child-sort", 123);
+}
diff --git a/src/tests/vm/page-merge-par.ck b/src/tests/vm/page-merge-par.ck
new file mode 100644
index 0000000..31f8aa7
--- /dev/null
+++ b/src/tests/vm/page-merge-par.ck
@@ -0,0 +1,29 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(page-merge-par) begin
+(page-merge-par) init
+(page-merge-par) sort chunk 0
+(page-merge-par) sort chunk 1
+(page-merge-par) sort chunk 2
+(page-merge-par) sort chunk 3
+(page-merge-par) sort chunk 4
+(page-merge-par) sort chunk 5
+(page-merge-par) sort chunk 6
+(page-merge-par) sort chunk 7
+(page-merge-par) wait for child 0
+(page-merge-par) wait for child 1
+(page-merge-par) wait for child 2
+(page-merge-par) wait for child 3
+(page-merge-par) wait for child 4
+(page-merge-par) wait for child 5
+(page-merge-par) wait for child 6
+(page-merge-par) wait for child 7
+(page-merge-par) merge
+(page-merge-par) verify
+(page-merge-par) success, buf_idx=1,048,576
+(page-merge-par) end
+EOF
+pass;
diff --git a/src/tests/vm/page-merge-seq.c b/src/tests/vm/page-merge-seq.c
new file mode 100644
index 0000000..12e3880
--- /dev/null
+++ b/src/tests/vm/page-merge-seq.c
@@ -0,0 +1,137 @@
+/* Generates about 1 MB of random data that is then divided into
+   16 chunks.  A separate subprocess sorts each chunk in
+   sequence.  Then we merge the chunks and verify that the result
+   is what it should be. */
+
+#include <syscall.h>
+#include "tests/arc4.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+/* This is the max file size for an older version of the Pintos
+   file system that had 126 direct blocks each pointing to a
+   single disk sector.  We could raise it now. */
+#define CHUNK_SIZE (126 * 512)
+#define CHUNK_CNT 16                            /* Number of chunks. */
+#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE)      /* Buffer size. */
+
+unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE];
+size_t histogram[256];
+
+/* Initialize buf1 with random data,
+   then count the number of instances of each value within it. */
+static void
+init (void) 
+{
+  struct arc4 arc4;
+  size_t i;
+
+  msg ("init");
+
+  arc4_init (&arc4, "foobar", 6);
+  arc4_crypt (&arc4, buf1, sizeof buf1);
+  for (i = 0; i < sizeof buf1; i++)
+    histogram[buf1[i]]++;
+}
+
+/* Sort each chunk of buf1 using a subprocess. */
+static void
+sort_chunks (void)
+{
+  size_t i;
+
+  create ("buffer", CHUNK_SIZE);
+  for (i = 0; i < CHUNK_CNT; i++) 
+    {
+      pid_t child;
+      int handle;
+
+      msg ("sort chunk %zu", i);
+
+      /* Write this chunk to a file. */
+      quiet = true;
+      CHECK ((handle = open ("buffer")) > 1, "open \"buffer\"");
+      write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
+      close (handle);
+
+      /* Sort with subprocess. */
+      CHECK ((child = exec ("child-sort buffer")) != -1,
+             "exec \"child-sort buffer\"");
+      CHECK (wait (child) == 123, "wait for child-sort");
+
+      /* Read chunk back from file. */
+      CHECK ((handle = open ("buffer")) > 1, "open \"buffer\"");
+      read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
+      close (handle);
+
+      quiet = false;
+    }
+}
+
+/* Merge the sorted chunks in buf1 into a fully sorted buf2. */
+static void
+merge (void) 
+{
+  unsigned char *mp[CHUNK_CNT];
+  size_t mp_left;
+  unsigned char *op;
+  size_t i;
+
+  msg ("merge");
+
+  /* Initialize merge pointers. */
+  mp_left = CHUNK_CNT;
+  for (i = 0; i < CHUNK_CNT; i++)
+    mp[i] = buf1 + CHUNK_SIZE * i;
+
+  /* Merge. */
+  op = buf2;
+  while (mp_left > 0) 
+    {
+      /* Find smallest value. */
+      size_t min = 0;
+      for (i = 1; i < mp_left; i++)
+        if (*mp[i] < *mp[min])
+          min = i;
+
+      /* Append value to buf2. */
+      *op++ = *mp[min];
+
+      /* Advance merge pointer.
+         Delete this chunk from the set if it's emptied. */ 
+      if ((++mp[min] - buf1) % CHUNK_SIZE == 0)
+        mp[min] = mp[--mp_left]; 
+    }
+}
+
+static void
+verify (void) 
+{
+  size_t buf_idx;
+  size_t hist_idx;
+
+  msg ("verify");
+
+  buf_idx = 0;
+  for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram;
+       hist_idx++)
+    {
+      while (histogram[hist_idx]-- > 0) 
+        {
+          if (buf2[buf_idx] != hist_idx)
+            fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx);
+          buf_idx++;
+        } 
+    }
+
+  msg ("success, buf_idx=%'zu", buf_idx);
+}
+
+void
+test_main (void)
+{
+  init ();
+  sort_chunks ();
+  merge ();
+  verify ();
+}
diff --git a/src/tests/vm/page-merge-seq.ck b/src/tests/vm/page-merge-seq.ck
new file mode 100644
index 0000000..d78f69d
--- /dev/null
+++ b/src/tests/vm/page-merge-seq.ck
@@ -0,0 +1,29 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(page-merge-seq) begin
+(page-merge-seq) init
+(page-merge-seq) sort chunk 0
+(page-merge-seq) sort chunk 1
+(page-merge-seq) sort chunk 2
+(page-merge-seq) sort chunk 3
+(page-merge-seq) sort chunk 4
+(page-merge-seq) sort chunk 5
+(page-merge-seq) sort chunk 6
+(page-merge-seq) sort chunk 7
+(page-merge-seq) sort chunk 8
+(page-merge-seq) sort chunk 9
+(page-merge-seq) sort chunk 10
+(page-merge-seq) sort chunk 11
+(page-merge-seq) sort chunk 12
+(page-merge-seq) sort chunk 13
+(page-merge-seq) sort chunk 14
+(page-merge-seq) sort chunk 15
+(page-merge-seq) merge
+(page-merge-seq) verify
+(page-merge-seq) success, buf_idx=1,032,192
+(page-merge-seq) end
+EOF
+pass;
diff --git a/src/tests/vm/page-merge-stk.c b/src/tests/vm/page-merge-stk.c
new file mode 100644
index 0000000..5eb1069
--- /dev/null
+++ b/src/tests/vm/page-merge-stk.c
@@ -0,0 +1,8 @@
+#include "tests/main.h"
+#include "tests/vm/parallel-merge.h"
+
+void
+test_main (void) 
+{
+  parallel_merge ("child-qsort", 72);
+}
diff --git a/src/tests/vm/page-merge-stk.ck b/src/tests/vm/page-merge-stk.ck
new file mode 100644
index 0000000..c5bc1ae
--- /dev/null
+++ b/src/tests/vm/page-merge-stk.ck
@@ -0,0 +1,29 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(page-merge-stk) begin
+(page-merge-stk) init
+(page-merge-stk) sort chunk 0
+(page-merge-stk) sort chunk 1
+(page-merge-stk) sort chunk 2
+(page-merge-stk) sort chunk 3
+(page-merge-stk) sort chunk 4
+(page-merge-stk) sort chunk 5
+(page-merge-stk) sort chunk 6
+(page-merge-stk) sort chunk 7
+(page-merge-stk) wait for child 0
+(page-merge-stk) wait for child 1
+(page-merge-stk) wait for child 2
+(page-merge-stk) wait for child 3
+(page-merge-stk) wait for child 4
+(page-merge-stk) wait for child 5
+(page-merge-stk) wait for child 6
+(page-merge-stk) wait for child 7
+(page-merge-stk) merge
+(page-merge-stk) verify
+(page-merge-stk) success, buf_idx=1,048,576
+(page-merge-stk) end
+EOF
+pass;
diff --git a/src/tests/vm/page-parallel.c b/src/tests/vm/page-parallel.c
new file mode 100644
index 0000000..9d619e0
--- /dev/null
+++ b/src/tests/vm/page-parallel.c
@@ -0,0 +1,21 @@
+/* Runs 4 child-linear processes at once. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define CHILD_CNT 4
+
+void
+test_main (void)
+{
+  pid_t children[CHILD_CNT];
+  int i;
+
+  for (i = 0; i < CHILD_CNT; i++) 
+    CHECK ((children[i] = exec ("child-linear")) != -1,
+           "exec \"child-linear\"");
+
+  for (i = 0; i < CHILD_CNT; i++) 
+    CHECK (wait (children[i]) == 0x42, "wait for child %d", i);
+}
diff --git a/src/tests/vm/page-parallel.ck b/src/tests/vm/page-parallel.ck
new file mode 100644
index 0000000..90c14ef
--- /dev/null
+++ b/src/tests/vm/page-parallel.ck
@@ -0,0 +1,17 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(page-parallel) begin
+(page-parallel) exec "child-linear"
+(page-parallel) exec "child-linear"
+(page-parallel) exec "child-linear"
+(page-parallel) exec "child-linear"
+(page-parallel) wait for child 0
+(page-parallel) wait for child 1
+(page-parallel) wait for child 2
+(page-parallel) wait for child 3
+(page-parallel) end
+EOF
+pass;
diff --git a/src/tests/vm/page-shuffle.c b/src/tests/vm/page-shuffle.c
new file mode 100644
index 0000000..095a9da
--- /dev/null
+++ b/src/tests/vm/page-shuffle.c
@@ -0,0 +1,30 @@
+/* Shuffles a 128 kB data buffer 10 times, printing the checksum
+   after each time. */
+
+#include <stdbool.h>
+#include "tests/arc4.h"
+#include "tests/cksum.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define SIZE (128 * 1024)
+
+static char buf[SIZE];
+
+void
+test_main (void)
+{
+  size_t i;
+
+  /* Initialize. */
+  for (i = 0; i < sizeof buf; i++)
+    buf[i] = i * 257;
+  msg ("init: cksum=%lu", cksum (buf, sizeof buf));
+    
+  /* Shuffle repeatedly. */
+  for (i = 0; i < 10; i++)
+    {
+      shuffle (buf, sizeof buf, 1);
+      msg ("shuffle %zu: cksum=%lu", i, cksum (buf, sizeof buf));
+    }
+}
diff --git a/src/tests/vm/page-shuffle.ck b/src/tests/vm/page-shuffle.ck
new file mode 100644
index 0000000..6447d38
--- /dev/null
+++ b/src/tests/vm/page-shuffle.ck
@@ -0,0 +1,44 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::cksum;
+use tests::lib;
+
+my ($init, @shuffle);
+if (1) {
+    # Use precalculated values.
+    $init = 3115322833;
+    @shuffle = (1691062564, 1973575879, 1647619479, 96566261, 3885786467,
+		3022003332, 3614934266, 2704001777, 735775156, 1864109763);
+} else {
+    # Recalculate values.
+    my ($buf) = "";
+    for my $i (0...128 * 1024 - 1) {
+	$buf .= chr (($i * 257) & 0xff);
+    }
+    $init = cksum ($buf);
+
+    random_init (0);
+    for my $i (1...10) {
+	$buf = shuffle ($buf, length ($buf), 1);
+	push (@shuffle, cksum ($buf));
+    }
+}
+
+check_expected (IGNORE_EXIT_CODES => 1, [<<EOF]);
+(page-shuffle) begin
+(page-shuffle) init: cksum=$init
+(page-shuffle) shuffle 0: cksum=$shuffle[0]
+(page-shuffle) shuffle 1: cksum=$shuffle[1]
+(page-shuffle) shuffle 2: cksum=$shuffle[2]
+(page-shuffle) shuffle 3: cksum=$shuffle[3]
+(page-shuffle) shuffle 4: cksum=$shuffle[4]
+(page-shuffle) shuffle 5: cksum=$shuffle[5]
+(page-shuffle) shuffle 6: cksum=$shuffle[6]
+(page-shuffle) shuffle 7: cksum=$shuffle[7]
+(page-shuffle) shuffle 8: cksum=$shuffle[8]
+(page-shuffle) shuffle 9: cksum=$shuffle[9]
+(page-shuffle) end
+EOF
+pass;
diff --git a/src/tests/vm/parallel-merge.c b/src/tests/vm/parallel-merge.c
new file mode 100644
index 0000000..cc09bb1
--- /dev/null
+++ b/src/tests/vm/parallel-merge.c
@@ -0,0 +1,149 @@
+/* Generates about 1 MB of random data that is then divided into
+   16 chunks.  A separate subprocess sorts each chunk; the
+   subprocesses run in parallel.  Then we merge the chunks and
+   verify that the result is what it should be. */
+
+#include "tests/vm/parallel-merge.h"
+#include <stdio.h>
+#include <syscall.h>
+#include "tests/arc4.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+#define CHUNK_SIZE (128 * 1024)
+#define CHUNK_CNT 8                             /* Number of chunks. */
+#define DATA_SIZE (CHUNK_CNT * CHUNK_SIZE)      /* Buffer size. */
+
+unsigned char buf1[DATA_SIZE], buf2[DATA_SIZE];
+size_t histogram[256];
+
+/* Initialize buf1 with random data,
+   then count the number of instances of each value within it. */
+static void
+init (void) 
+{
+  struct arc4 arc4;
+  size_t i;
+
+  msg ("init");
+
+  arc4_init (&arc4, "foobar", 6);
+  arc4_crypt (&arc4, buf1, sizeof buf1);
+  for (i = 0; i < sizeof buf1; i++)
+    histogram[buf1[i]]++;
+}
+
+/* Sort each chunk of buf1 using SUBPROCESS,
+   which is expected to return EXIT_STATUS. */
+static void
+sort_chunks (const char *subprocess, int exit_status)
+{
+  pid_t children[CHUNK_CNT];
+  size_t i;
+
+  for (i = 0; i < CHUNK_CNT; i++) 
+    {
+      char fn[128];
+      char cmd[128];
+      int handle;
+
+      msg ("sort chunk %zu", i);
+
+      /* Write this chunk to a file. */
+      snprintf (fn, sizeof fn, "buf%zu", i);
+      create (fn, CHUNK_SIZE);
+      quiet = true;
+      CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
+      write (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
+      close (handle);
+
+      /* Sort with subprocess. */
+      snprintf (cmd, sizeof cmd, "%s %s", subprocess, fn);
+      CHECK ((children[i] = exec (cmd)) != -1, "exec \"%s\"", cmd);
+      quiet = false;
+    }
+
+  for (i = 0; i < CHUNK_CNT; i++) 
+    {
+      char fn[128];
+      int handle;
+
+      CHECK (wait (children[i]) == exit_status, "wait for child %zu", i);
+
+      /* Read chunk back from file. */
+      quiet = true;
+      snprintf (fn, sizeof fn, "buf%zu", i);
+      CHECK ((handle = open (fn)) > 1, "open \"%s\"", fn);
+      read (handle, buf1 + CHUNK_SIZE * i, CHUNK_SIZE);
+      close (handle);
+      quiet = false;
+    }
+}
+
+/* Merge the sorted chunks in buf1 into a fully sorted buf2. */
+static void
+merge (void) 
+{
+  unsigned char *mp[CHUNK_CNT];
+  size_t mp_left;
+  unsigned char *op;
+  size_t i;
+
+  msg ("merge");
+
+  /* Initialize merge pointers. */
+  mp_left = CHUNK_CNT;
+  for (i = 0; i < CHUNK_CNT; i++)
+    mp[i] = buf1 + CHUNK_SIZE * i;
+
+  /* Merge. */
+  op = buf2;
+  while (mp_left > 0) 
+    {
+      /* Find smallest value. */
+      size_t min = 0;
+      for (i = 1; i < mp_left; i++)
+        if (*mp[i] < *mp[min])
+          min = i;
+
+      /* Append value to buf2. */
+      *op++ = *mp[min];
+
+      /* Advance merge pointer.
+         Delete this chunk from the set if it's emptied. */
+      if ((++mp[min] - buf1) % CHUNK_SIZE == 0) 
+        mp[min] = mp[--mp_left];
+    }
+}
+
+static void
+verify (void) 
+{
+  size_t buf_idx;
+  size_t hist_idx;
+
+  msg ("verify");
+
+  buf_idx = 0;
+  for (hist_idx = 0; hist_idx < sizeof histogram / sizeof *histogram;
+       hist_idx++)
+    {
+      while (histogram[hist_idx]-- > 0) 
+        {
+          if (buf2[buf_idx] != hist_idx)
+            fail ("bad value %d in byte %zu", buf2[buf_idx], buf_idx);
+          buf_idx++;
+        } 
+    }
+
+  msg ("success, buf_idx=%'zu", buf_idx);
+}
+
+void
+parallel_merge (const char *child_name, int exit_status)
+{
+  init ();
+  sort_chunks (child_name, exit_status);
+  merge ();
+  verify ();
+}
diff --git a/src/tests/vm/parallel-merge.h b/src/tests/vm/parallel-merge.h
new file mode 100644
index 0000000..a6b6431
--- /dev/null
+++ b/src/tests/vm/parallel-merge.h
@@ -0,0 +1,6 @@
+#ifndef TESTS_VM_PARALLEL_MERGE
+#define TESTS_VM_PARALLEL_MERGE 1
+
+void parallel_merge (const char *child_name, int exit_status);
+
+#endif /* tests/vm/parallel-merge.h */
diff --git a/src/tests/vm/process_death.pm b/src/tests/vm/process_death.pm
new file mode 100644
index 0000000..52039a1
--- /dev/null
+++ b/src/tests/vm/process_death.pm
@@ -0,0 +1,22 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+
+sub check_process_death {
+    my ($proc_name) = @_;
+    our ($test);
+    my (@output) = read_text_file ("$test.output");
+
+    common_checks ("run", @output);
+    @output = get_core_output ("run", @output);
+    fail "First line of output is not `($proc_name) begin' message.\n"
+      if $output[0] ne "($proc_name) begin";
+    fail "Output missing '$proc_name: exit(-1)' message.\n"
+      if !grep ("$proc_name: exit(-1)" eq $_, @output);
+    fail "Output contains '($proc_name) end' message.\n"
+      if grep (/\($proc_name\) end/, @output);
+    pass;
+}
+
+1;
diff --git a/src/tests/vm/pt-bad-addr.c b/src/tests/vm/pt-bad-addr.c
new file mode 100644
index 0000000..3ca4084
--- /dev/null
+++ b/src/tests/vm/pt-bad-addr.c
@@ -0,0 +1,11 @@
+/* Accesses a bad address.
+   The process must be terminated with -1 exit code. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  fail ("bad addr read as %d", *(int *) 0x04000000);
+}
diff --git a/src/tests/vm/pt-bad-addr.ck b/src/tests/vm/pt-bad-addr.ck
new file mode 100644
index 0000000..09ea039
--- /dev/null
+++ b/src/tests/vm/pt-bad-addr.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::vm::process_death;
+
+check_process_death ('pt-bad-addr');
diff --git a/src/tests/vm/pt-bad-read.c b/src/tests/vm/pt-bad-read.c
new file mode 100644
index 0000000..ee791ff
--- /dev/null
+++ b/src/tests/vm/pt-bad-read.c
@@ -0,0 +1,16 @@
+/* Reads from a file into a bad address.
+   The process must be terminated with -1 exit code. */
+
+#include <syscall.h>
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  int handle;
+
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  read (handle, (char *) &handle - 4096, 1);
+  fail ("survived reading data into bad address");
+}
diff --git a/src/tests/vm/pt-bad-read.ck b/src/tests/vm/pt-bad-read.ck
new file mode 100644
index 0000000..1f96bb4
--- /dev/null
+++ b/src/tests/vm/pt-bad-read.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(pt-bad-read) begin
+(pt-bad-read) open "sample.txt"
+pt-bad-read: exit(-1)
+EOF
+pass;
diff --git a/src/tests/vm/pt-big-stk-obj.c b/src/tests/vm/pt-big-stk-obj.c
new file mode 100644
index 0000000..6b630ec
--- /dev/null
+++ b/src/tests/vm/pt-big-stk-obj.c
@@ -0,0 +1,20 @@
+/* Allocates and writes to a 64 kB object on the stack.
+   This must succeed. */
+
+#include <string.h>
+#include "tests/arc4.h"
+#include "tests/cksum.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  char stk_obj[65536];
+  struct arc4 arc4;
+
+  arc4_init (&arc4, "foobar", 6);
+  memset (stk_obj, 0, sizeof stk_obj);
+  arc4_crypt (&arc4, stk_obj, sizeof stk_obj);
+  msg ("cksum: %lu", cksum (stk_obj, sizeof stk_obj));
+}
diff --git a/src/tests/vm/pt-big-stk-obj.ck b/src/tests/vm/pt-big-stk-obj.ck
new file mode 100644
index 0000000..eb5853a
--- /dev/null
+++ b/src/tests/vm/pt-big-stk-obj.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(pt-big-stk-obj) begin
+(pt-big-stk-obj) cksum: 3256410166
+(pt-big-stk-obj) end
+EOF
+pass;
diff --git a/src/tests/vm/pt-grow-bad.c b/src/tests/vm/pt-grow-bad.c
new file mode 100644
index 0000000..d4beba2
--- /dev/null
+++ b/src/tests/vm/pt-grow-bad.c
@@ -0,0 +1,14 @@
+/* Read from an address 4,096 bytes below the stack pointer.
+   The process must be terminated with -1 exit code. */
+
+#include <string.h>
+#include "tests/arc4.h"
+#include "tests/cksum.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  asm volatile ("movl -4096(%esp), %eax");
+}
diff --git a/src/tests/vm/pt-grow-bad.ck b/src/tests/vm/pt-grow-bad.ck
new file mode 100644
index 0000000..4c0ab8a
--- /dev/null
+++ b/src/tests/vm/pt-grow-bad.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_USER_FAULTS => 1, [<<'EOF']);
+(pt-grow-bad) begin
+pt-grow-bad: exit(-1)
+EOF
+pass;
diff --git a/src/tests/vm/pt-grow-pusha.c b/src/tests/vm/pt-grow-pusha.c
new file mode 100644
index 0000000..f9762a5
--- /dev/null
+++ b/src/tests/vm/pt-grow-pusha.c
@@ -0,0 +1,20 @@
+/* Expand the stack by 32 bytes all at once using the PUSHA
+   instruction.
+   This must succeed. */
+
+#include <string.h>
+#include "tests/arc4.h"
+#include "tests/cksum.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  asm volatile
+    ("movl %%esp, %%eax;"        /* Save a copy of the stack pointer. */
+     "andl $0xfffff000, %%esp;"  /* Move stack pointer to bottom of page. */
+     "pushal;"                   /* Push 32 bytes on stack at once. */
+     "movl %%eax, %%esp"         /* Restore copied stack pointer. */
+     : : : "eax");               /* Tell GCC we destroyed eax. */
+}
diff --git a/src/tests/vm/pt-grow-pusha.ck b/src/tests/vm/pt-grow-pusha.ck
new file mode 100644
index 0000000..5000966
--- /dev/null
+++ b/src/tests/vm/pt-grow-pusha.ck
@@ -0,0 +1,9 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(pt-grow-pusha) begin
+(pt-grow-pusha) end
+EOF
+pass;
diff --git a/src/tests/vm/pt-grow-stack.c b/src/tests/vm/pt-grow-stack.c
new file mode 100644
index 0000000..0997a00
--- /dev/null
+++ b/src/tests/vm/pt-grow-stack.c
@@ -0,0 +1,20 @@
+/* Demonstrate that the stack can grow.
+   This must succeed. */
+
+#include <string.h>
+#include "tests/arc4.h"
+#include "tests/cksum.h"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  char stack_obj[4096];
+  struct arc4 arc4;
+
+  arc4_init (&arc4, "foobar", 6);
+  memset (stack_obj, 0, sizeof stack_obj);
+  arc4_crypt (&arc4, stack_obj, sizeof stack_obj);
+  msg ("cksum: %lu", cksum (stack_obj, sizeof stack_obj));
+}
diff --git a/src/tests/vm/pt-grow-stack.ck b/src/tests/vm/pt-grow-stack.ck
new file mode 100644
index 0000000..1e669db
--- /dev/null
+++ b/src/tests/vm/pt-grow-stack.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(pt-grow-stack) begin
+(pt-grow-stack) cksum: 3424492700
+(pt-grow-stack) end
+EOF
+pass;
diff --git a/src/tests/vm/pt-grow-stk-sc.c b/src/tests/vm/pt-grow-stk-sc.c
new file mode 100644
index 0000000..3efbb5f
--- /dev/null
+++ b/src/tests/vm/pt-grow-stk-sc.c
@@ -0,0 +1,32 @@
+/* This test checks that the stack is properly extended even if
+   the first access to a stack location occurs inside a system
+   call.
+
+   From Godmar Back. */
+
+#include <string.h>
+#include <syscall.h>
+#include "tests/vm/sample.inc"
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  int handle;
+  int slen = strlen (sample);
+  char buf2[65536];
+
+  /* Write file via write(). */
+  CHECK (create ("sample.txt", slen), "create \"sample.txt\"");
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  CHECK (write (handle, sample, slen) == slen, "write \"sample.txt\"");
+  close (handle);
+
+  /* Read back via read(). */
+  CHECK ((handle = open ("sample.txt")) > 1, "2nd open \"sample.txt\"");
+  CHECK (read (handle, buf2 + 32768, slen) == slen, "read \"sample.txt\"");
+
+  CHECK (!memcmp (sample, buf2 + 32768, slen), "compare written data against read data");
+  close (handle);
+}
diff --git a/src/tests/vm/pt-grow-stk-sc.ck b/src/tests/vm/pt-grow-stk-sc.ck
new file mode 100644
index 0000000..23d3b02
--- /dev/null
+++ b/src/tests/vm/pt-grow-stk-sc.ck
@@ -0,0 +1,15 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected (IGNORE_EXIT_CODES => 1, [<<'EOF']);
+(pt-grow-stk-sc) begin
+(pt-grow-stk-sc) create "sample.txt"
+(pt-grow-stk-sc) open "sample.txt"
+(pt-grow-stk-sc) write "sample.txt"
+(pt-grow-stk-sc) 2nd open "sample.txt"
+(pt-grow-stk-sc) read "sample.txt"
+(pt-grow-stk-sc) compare written data against read data
+(pt-grow-stk-sc) end
+EOF
+pass;
diff --git a/src/tests/vm/pt-write-code-2.c b/src/tests/vm/pt-write-code-2.c
new file mode 100644
index 0000000..83bcc2c
--- /dev/null
+++ b/src/tests/vm/pt-write-code-2.c
@@ -0,0 +1,15 @@
+/* Try to write to the code segment using a system call.
+   The process must be terminated with -1 exit code. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  int handle;
+
+  CHECK ((handle = open ("sample.txt")) > 1, "open \"sample.txt\"");
+  read (handle, (void *) test_main, 1);
+  fail ("survived reading data into code segment");
+}
diff --git a/src/tests/vm/pt-write-code.c b/src/tests/vm/pt-write-code.c
new file mode 100644
index 0000000..5072cec
--- /dev/null
+++ b/src/tests/vm/pt-write-code.c
@@ -0,0 +1,12 @@
+/* Try to write to the code segment.
+   The process must be terminated with -1 exit code. */
+
+#include "tests/lib.h"
+#include "tests/main.h"
+
+void
+test_main (void)
+{
+  *(int *) test_main = 0;
+  fail ("writing the code segment succeeded");
+}
diff --git a/src/tests/vm/pt-write-code.ck b/src/tests/vm/pt-write-code.ck
new file mode 100644
index 0000000..65610fb
--- /dev/null
+++ b/src/tests/vm/pt-write-code.ck
@@ -0,0 +1,7 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+use tests::vm::process_death;
+
+check_process_death ('pt-write-code');
diff --git a/src/tests/vm/pt-write-code2.ck b/src/tests/vm/pt-write-code2.ck
new file mode 100644
index 0000000..69ffc77
--- /dev/null
+++ b/src/tests/vm/pt-write-code2.ck
@@ -0,0 +1,10 @@
+# -*- perl -*-
+use strict;
+use warnings;
+use tests::tests;
+check_expected ([<<'EOF']);
+(pt-write-code2) begin
+(pt-write-code2) open "sample.txt"
+pt-write-code2: exit(-1)
+EOF
+pass;
diff --git a/src/tests/vm/qsort.c b/src/tests/vm/qsort.c
new file mode 100644
index 0000000..922572c
--- /dev/null
+++ b/src/tests/vm/qsort.c
@@ -0,0 +1,136 @@
+#include "tests/vm/qsort.h"
+#include <stdbool.h>
+#include <debug.h>
+#include <random.h>
+
+/* Picks a pivot for the quicksort from the SIZE bytes in BUF. */
+static unsigned char
+pick_pivot (unsigned char *buf, size_t size) 
+{
+  ASSERT (size >= 1);
+  return buf[random_ulong () % size];
+}
+
+/* Checks whether the SIZE bytes in ARRAY are divided into an
+   initial LEFT_SIZE elements all less than PIVOT followed by
+   SIZE - LEFT_SIZE elements all greater than or equal to
+   PIVOT. */
+static bool
+is_partitioned (const unsigned char *array, size_t size,
+                unsigned char pivot, size_t left_size) 
+{
+  size_t i;
+  
+  for (i = 0; i < left_size; i++)
+    if (array[i] >= pivot)
+      return false;
+
+  for (; i < size; i++)
+    if (array[i] < pivot)
+      return false;
+
+  return true;
+}
+
+/* Swaps the bytes at *A and *B. */
+static void
+swap (unsigned char *a, unsigned char *b) 
+{
+  unsigned char t = *a;
+  *a = *b;
+  *b = t;
+}
+
+/* Partitions ARRAY in-place in an initial run of bytes all less
+   than PIVOT, followed by a run of bytes all greater than or
+   equal to PIVOT.  Returns the length of the initial run. */
+static size_t
+partition (unsigned char *array, size_t size, int pivot) 
+{
+  size_t left_size = size;
+  unsigned char *first = array;
+  unsigned char *last = first + left_size;
+
+  for (;;)
+    {
+      /* Move FIRST forward to point to first element greater than
+         PIVOT. */
+      for (;;)
+        {
+          if (first == last)
+            {
+              ASSERT (is_partitioned (array, size, pivot, left_size));
+              return left_size;
+            }
+          else if (*first >= pivot)
+            break;
+
+          first++;
+        }
+      left_size--;
+
+      /* Move LAST backward to point to last element no bigger
+         than PIVOT. */
+      for (;;)
+        {
+          last--;
+
+          if (first == last)
+            {
+              ASSERT (is_partitioned (array, size, pivot, left_size));
+              return left_size;
+            }
+          else if (*last < pivot)
+            break;
+          else
+            left_size--;
+        }
+
+      /* By swapping FIRST and LAST we extend the starting and
+         ending sequences that pass and fail, respectively,
+         PREDICATE. */
+      swap (first, last);
+      first++;
+    }
+}
+
+/* Returns true if the SIZE bytes in BUF are in nondecreasing
+   order, false otherwise. */
+static bool
+is_sorted (const unsigned char *buf, size_t size) 
+{
+  size_t i;
+
+  for (i = 1; i < size; i++)
+    if (buf[i - 1] > buf[i])
+      return false;
+
+  return true;
+}
+
+/* Sorts the SIZE bytes in BUF into nondecreasing order, using
+   the quick-sort algorithm. */
+void
+qsort_bytes (unsigned char *buf, size_t size) 
+{
+  if (!is_sorted (buf, size)) 
+    {
+      int pivot = pick_pivot (buf, size);
+
+      unsigned char *left_half = buf;
+      size_t left_size = partition (buf, size, pivot);
+      unsigned char *right_half = left_half + left_size;
+      size_t right_size = size - left_size;
+  
+      if (left_size <= right_size) 
+        {
+          qsort_bytes (left_half, left_size);
+          qsort_bytes (right_half, right_size); 
+        }
+      else
+        {
+          qsort_bytes (right_half, right_size); 
+          qsort_bytes (left_half, left_size);
+        }
+    } 
+}
diff --git a/src/tests/vm/qsort.h b/src/tests/vm/qsort.h
new file mode 100644
index 0000000..61b65f3
--- /dev/null
+++ b/src/tests/vm/qsort.h
@@ -0,0 +1,8 @@
+#ifndef TESTS_VM_QSORT_H
+#define TESTS_VM_QSORT_H 1
+
+#include <stddef.h>
+
+void qsort_bytes (unsigned char *buf, size_t size);
+
+#endif /* tests/vm/qsort.h */
diff --git a/src/tests/vm/sample.inc b/src/tests/vm/sample.inc
new file mode 100644
index 0000000..a60a139
--- /dev/null
+++ b/src/tests/vm/sample.inc
@@ -0,0 +1,19 @@
+char sample[] = {
+  "===  ALL USERS PLEASE NOTE  ========================\n"
+  "\n"
+  "CAR and CDR now return extra values.\n"
+  "\n"
+  "The function CAR now returns two values.  Since it has to go to the\n"
+  "trouble to figure out if the object is carcdr-able anyway, we figured\n"
+  "you might as well get both halves at once.  For example, the following\n"
+  "code shows how to destructure a cons (SOME-CONS) into its two slots\n"
+  "(THE-CAR and THE-CDR):\n"
+  "\n"
+  "        (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)\n"
+  "\n"
+  "For symmetry with CAR, CDR returns a second value which is the CAR of\n"
+  "the object.  In a related change, the functions MAKE-ARRAY and CONS\n"
+  "have been fixed so they don't allocate any storage except on the\n"
+  "stack.  This should hopefully help people who don't like using the\n"
+  "garbage collector because it cold boots the machine so often.\n"
+};
diff --git a/src/tests/vm/sample.txt b/src/tests/vm/sample.txt
new file mode 100644
index 0000000..c446830
--- /dev/null
+++ b/src/tests/vm/sample.txt
@@ -0,0 +1,17 @@
+===  ALL USERS PLEASE NOTE  ========================
+
+CAR and CDR now return extra values.
+
+The function CAR now returns two values.  Since it has to go to the
+trouble to figure out if the object is carcdr-able anyway, we figured
+you might as well get both halves at once.  For example, the following
+code shows how to destructure a cons (SOME-CONS) into its two slots
+(THE-CAR and THE-CDR):
+
+        (MULTIPLE-VALUE-BIND (THE-CAR THE-CDR) (CAR SOME-CONS) ...)
+
+For symmetry with CAR, CDR returns a second value which is the CAR of
+the object.  In a related change, the functions MAKE-ARRAY and CONS
+have been fixed so they don't allocate any storage except on the
+stack.  This should hopefully help people who don't like using the
+garbage collector because it cold boots the machine so often.
diff --git a/src/threads/.gitignore b/src/threads/.gitignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/src/threads/.gitignore
@@ -0,0 +1,3 @@
+build
+bochsrc.txt
+bochsout.txt
diff --git a/src/threads/Make.vars b/src/threads/Make.vars
new file mode 100644
index 0000000..310c240
--- /dev/null
+++ b/src/threads/Make.vars
@@ -0,0 +1,7 @@
+# -*- makefile -*-
+
+kernel.bin: DEFINES =
+KERNEL_SUBDIRS = threads devices lib lib/kernel $(TEST_SUBDIRS)
+TEST_SUBDIRS = tests/threads
+GRADING_FILE = $(SRCDIR)/tests/threads/Grading
+SIMULATOR = --bochs
diff --git a/src/threads/Makefile b/src/threads/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/src/threads/Makefile
@@ -0,0 +1 @@
+include ../Makefile.kernel
diff --git a/src/threads/flags.h b/src/threads/flags.h
new file mode 100644
index 0000000..5654ac7
--- /dev/null
+++ b/src/threads/flags.h
@@ -0,0 +1,8 @@
+#ifndef THREADS_FLAGS_H
+#define THREADS_FLAGS_H
+
+/* EFLAGS Register. */
+#define FLAG_MBS  0x00000002    /* Must be set. */
+#define FLAG_IF   0x00000200    /* Interrupt Flag. */
+
+#endif /* threads/flags.h */
diff --git a/src/threads/init.c b/src/threads/init.c
new file mode 100644
index 0000000..2ed14fc
--- /dev/null
+++ b/src/threads/init.c
@@ -0,0 +1,446 @@
+#include "threads/init.h"
+#include <console.h>
+#include <debug.h>
+#include <inttypes.h>
+#include <limits.h>
+#include <random.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "devices/kbd.h"
+#include "devices/input.h"
+#include "devices/serial.h"
+#include "devices/shutdown.h"
+#include "devices/timer.h"
+#include "devices/vga.h"
+#include "devices/rtc.h"
+#include "threads/interrupt.h"
+#include "threads/io.h"
+#include "threads/loader.h"
+#include "threads/malloc.h"
+#include "threads/palloc.h"
+#include "threads/pte.h"
+#include "threads/thread.h"
+#ifdef USERPROG
+#include "userprog/process.h"
+#include "userprog/exception.h"
+#include "userprog/gdt.h"
+#include "userprog/syscall.h"
+#include "userprog/tss.h"
+#else
+#include "tests/threads/tests.h"
+#include "../devices/shutdown.h"
+#endif
+
+#ifdef VM
+#include "vm/frame.h"
+#include "vm/page.h"
+#include <vm/swap.h>
+#endif
+
+#ifdef FILESYS
+#include "devices/block.h"
+#include "devices/ide.h"
+#include "filesys/filesys.h"
+#include "filesys/fsutil.h"
+#endif
+
+/* Page directory with kernel mappings only. */
+uint32_t *init_page_dir;
+
+#ifdef FILESYS
+/* -f: Format the file system? */
+static bool format_filesys;
+
+/* -filesys, -scratch, -swap: Names of block devices to use,
+   overriding the defaults. */
+static const char *filesys_bdev_name;
+static const char *scratch_bdev_name;
+#ifdef VM
+static const char *swap_bdev_name;
+#endif
+#endif /* FILESYS */
+
+/* -ul: Maximum number of pages to put into palloc's user pool. */
+static size_t user_page_limit = SIZE_MAX;
+
+static void bss_init (void);
+static void paging_init (void);
+
+static char **read_command_line (void);
+static char **parse_options (char **argv);
+static void run_actions (char **argv);
+static void usage (void);
+
+#ifdef FILESYS
+static void locate_block_devices (void);
+static void locate_block_device (enum block_type, const char *name);
+#endif
+
+int main (void) NO_RETURN;
+
+/* Pintos main program. */
+int
+main (void)
+{
+  char **argv;
+
+  /* Clear BSS. */  
+  bss_init ();
+
+  /* Break command line into arguments and parse options. */
+  argv = read_command_line ();
+  argv = parse_options (argv);
+
+  /* Initialize ourselves as a thread so we can use locks,
+     then enable console locking. */
+  thread_init ();
+  console_init ();  
+
+  /* Greet user. */
+  printf ("Pintos booting with %'"PRIu32" kB RAM...\n",
+          init_ram_pages * PGSIZE / 1024);
+
+  /* Initialize memory system. */
+  palloc_init (user_page_limit);
+  malloc_init ();
+  paging_init ();
+
+  /* Segmentation. */
+#ifdef USERPROG
+  tss_init ();
+  gdt_init ();
+#endif
+
+  /* Initialize interrupt handlers. */
+  intr_init ();
+  timer_init ();
+  kbd_init ();
+  input_init ();
+#ifdef USERPROG
+  exception_init ();
+  syscall_init ();
+#endif
+
+  /* Start thread scheduler and enable interrupts. */
+  thread_start ();
+  serial_init_queue ();
+  timer_calibrate ();
+
+#ifdef FILESYS
+  /* Initialize file system. */
+  ide_init ();
+  locate_block_devices ();
+  filesys_init (format_filesys);
+  /* ----- Newly Add for Proj04 FileSys ----- */
+  set_main_thread_dir();
+  /* ----- Newly Add for Proj04 FileSys ----- */
+#endif
+
+#ifdef VM
+  page_lock_init();
+  frame_init();
+  swap_init();
+#endif
+
+  printf ("Boot complete.\n");
+  
+  /* Run actions specified on kernel command line. */
+  run_actions (argv);
+
+  /* Finish up. */
+  shutdown ();
+  thread_exit ();
+}
+
+/* Clear the "BSS", a segment that should be initialized to
+   zeros.  It isn't actually stored on disk or zeroed by the
+   kernel loader, so we have to zero it ourselves.
+
+   The start and end of the BSS segment is recorded by the
+   linker as _start_bss and _end_bss.  See kernel.lds. */
+static void
+bss_init (void) 
+{
+  extern char _start_bss, _end_bss;
+  memset (&_start_bss, 0, &_end_bss - &_start_bss);
+}
+
+/* Populates the base page directory and page table with the
+   kernel virtual mapping, and then sets up the CPU to use the
+   new page directory.  Points init_page_dir to the page
+   directory it creates. */
+static void
+paging_init (void)
+{
+  uint32_t *pd, *pt;
+  size_t page;
+  extern char _start, _end_kernel_text;
+
+  pd = init_page_dir = palloc_get_page (PAL_ASSERT | PAL_ZERO);
+  pt = NULL;
+  for (page = 0; page < init_ram_pages; page++)
+    {
+      uintptr_t paddr = page * PGSIZE;
+      char *vaddr = ptov (paddr);
+      size_t pde_idx = pd_no (vaddr);
+      size_t pte_idx = pt_no (vaddr);
+      bool in_kernel_text = &_start <= vaddr && vaddr < &_end_kernel_text;
+
+      if (pd[pde_idx] == 0)
+        {
+          pt = palloc_get_page (PAL_ASSERT | PAL_ZERO);
+          pd[pde_idx] = pde_create (pt);
+        }
+
+      pt[pte_idx] = pte_create_kernel (vaddr, !in_kernel_text);
+    }
+
+  /* Store the physical address of the page directory into CR3
+     aka PDBR (page directory base register).  This activates our
+     new page tables immediately.  See [IA32-v2a] "MOV--Move
+     to/from Control Registers" and [IA32-v3a] 3.7.5 "Base Address
+     of the Page Directory". */
+  asm volatile ("movl %0, %%cr3" : : "r" (vtop (init_page_dir)));
+}
+
+/* Breaks the kernel command line into words and returns them as
+   an argv-like array. */
+static char **
+read_command_line (void) 
+{
+  static char *argv[LOADER_ARGS_LEN / 2 + 1];
+  char *p, *end;
+  int argc;
+  int i;
+
+  argc = *(uint32_t *) ptov (LOADER_ARG_CNT);
+  p = ptov (LOADER_ARGS);
+  end = p + LOADER_ARGS_LEN;
+  for (i = 0; i < argc; i++) 
+    {
+      if (p >= end)
+        PANIC ("command line arguments overflow");
+
+      argv[i] = p;
+      p += strnlen (p, end - p) + 1;
+    }
+  argv[argc] = NULL;
+
+  /* Print kernel command line. */
+  printf ("Kernel command line:");
+  for (i = 0; i < argc; i++)
+    if (strchr (argv[i], ' ') == NULL)
+      printf (" %s", argv[i]);
+    else
+      printf (" '%s'", argv[i]);
+  printf ("\n");
+
+  return argv;
+}
+
+/* Parses options in ARGV[]
+   and returns the first non-option argument. */
+static char **
+parse_options (char **argv) 
+{
+  for (; *argv != NULL && **argv == '-'; argv++)
+    {
+      char *save_ptr;
+      char *name = strtok_r (*argv, "=", &save_ptr);
+      char *value = strtok_r (NULL, "", &save_ptr);
+      
+      if (!strcmp (name, "-h"))
+        usage ();
+      else if (!strcmp (name, "-q"))
+        shutdown_configure (SHUTDOWN_POWER_OFF);
+      else if (!strcmp (name, "-r"))
+        shutdown_configure (SHUTDOWN_REBOOT);
+#ifdef FILESYS
+      else if (!strcmp (name, "-f"))
+        format_filesys = true;
+      else if (!strcmp (name, "-filesys"))
+        filesys_bdev_name = value;
+      else if (!strcmp (name, "-scratch"))
+        scratch_bdev_name = value;
+#ifdef VM
+      else if (!strcmp (name, "-swap"))
+        swap_bdev_name = value;
+#endif
+#endif
+      else if (!strcmp (name, "-rs"))
+        random_init (atoi (value));
+      else if (!strcmp (name, "-mlfqs"))
+        thread_mlfqs = true;
+#ifdef USERPROG
+      else if (!strcmp (name, "-ul"))
+        user_page_limit = atoi (value);
+#endif
+      else
+        PANIC ("unknown option `%s' (use -h for help)", name);
+    }
+
+  /* Initialize the random number generator based on the system
+     time.  This has no effect if an "-rs" option was specified.
+
+     When running under Bochs, this is not enough by itself to
+     get a good seed value, because the pintos script sets the
+     initial time to a predictable value, not to the local time,
+     for reproducibility.  To fix this, give the "-r" option to
+     the pintos script to request real-time execution. */
+  random_init (rtc_get_time ());
+  
+  return argv;
+}
+
+/* Runs the task specified in ARGV[1]. */
+static void
+run_task (char **argv)
+{
+  const char *task = argv[1];
+  
+  printf ("Executing '%s':\n", task);
+#ifdef USERPROG
+  process_wait (process_execute (task));
+#else
+  run_test (task);
+#endif
+  printf ("Execution of '%s' complete.\n", task);
+}
+
+/* Executes all of the actions specified in ARGV[]
+   up to the null pointer sentinel. */
+static void
+run_actions (char **argv) 
+{
+  /* An action. */
+  struct action 
+    {
+      char *name;                       /* Action name. */
+      int argc;                         /* # of args, including action name. */
+      void (*function) (char **argv);   /* Function to execute action. */
+    };
+
+  /* Table of supported actions. */
+  static const struct action actions[] = 
+    {
+      {"run", 2, run_task},
+#ifdef FILESYS
+      {"ls", 1, fsutil_ls},
+      {"cat", 2, fsutil_cat},
+      {"rm", 2, fsutil_rm},
+      {"extract", 1, fsutil_extract},
+      {"append", 2, fsutil_append},
+#endif
+      {NULL, 0, NULL},
+    };
+
+  while (*argv != NULL)
+    {
+      const struct action *a;
+      int i;
+
+      /* Find action name. */
+      for (a = actions; ; a++)
+        if (a->name == NULL)
+          PANIC ("unknown action `%s' (use -h for help)", *argv);
+        else if (!strcmp (*argv, a->name))
+          break;
+
+      /* Check for required arguments. */
+      for (i = 1; i < a->argc; i++)
+        if (argv[i] == NULL)
+          PANIC ("action `%s' requires %d argument(s)", *argv, a->argc - 1);
+
+      /* Invoke action and advance. */
+      a->function (argv);
+      argv += a->argc;
+    }
+  
+}
+
+/* Prints a kernel command line help message and powers off the
+   machine. */
+static void
+usage (void)
+{
+  printf ("\nCommand line syntax: [OPTION...] [ACTION...]\n"
+          "Options must precede actions.\n"
+          "Actions are executed in the order specified.\n"
+          "\nAvailable actions:\n"
+#ifdef USERPROG
+          "  run 'PROG [ARG...]' Run PROG and wait for it to complete.\n"
+#else
+          "  run TEST           Run TEST.\n"
+#endif
+#ifdef FILESYS
+          "  ls                 List files in the root directory.\n"
+          "  cat FILE           Print FILE to the console.\n"
+          "  rm FILE            Delete FILE.\n"
+          "Use these actions indirectly via `pintos' -g and -p options:\n"
+          "  extract            Untar from scratch device into file system.\n"
+          "  append FILE        Append FILE to tar file on scratch device.\n"
+#endif
+          "\nOptions:\n"
+          "  -h                 Print this help message and power off.\n"
+          "  -q                 Power off VM after actions or on panic.\n"
+          "  -r                 Reboot after actions.\n"
+#ifdef FILESYS
+          "  -f                 Format file system device during startup.\n"
+          "  -filesys=BDEV      Use BDEV for file system instead of default.\n"
+          "  -scratch=BDEV      Use BDEV for scratch instead of default.\n"
+#ifdef VM
+          "  -swap=BDEV         Use BDEV for swap instead of default.\n"
+#endif
+#endif
+          "  -rs=SEED           Set random number seed to SEED.\n"
+          "  -mlfqs             Use multi-level feedback queue scheduler.\n"
+#ifdef USERPROG
+          "  -ul=COUNT          Limit user memory to COUNT pages.\n"
+#endif
+          );
+  shutdown_power_off ();
+}
+
+#ifdef FILESYS
+/* Figure out what block devices to cast in the various Pintos roles. */
+static void
+locate_block_devices (void)
+{
+  locate_block_device (BLOCK_FILESYS, filesys_bdev_name);
+  locate_block_device (BLOCK_SCRATCH, scratch_bdev_name);
+#ifdef VM
+  locate_block_device (BLOCK_SWAP, swap_bdev_name);
+#endif
+}
+
+/* Figures out what block device to use for the given ROLE: the
+   block device with the given NAME, if NAME is non-null,
+   otherwise the first block device in probe order of type
+   ROLE. */
+static void
+locate_block_device (enum block_type role, const char *name)
+{
+  struct block *block = NULL;
+
+  if (name != NULL)
+    {
+      block = block_get_by_name (name);
+      if (block == NULL)
+        PANIC ("No such block device \"%s\"", name);
+    }
+  else
+    {
+      for (block = block_first (); block != NULL; block = block_next (block))
+        if (block_type (block) == role)
+          break;
+    }
+
+  if (block != NULL)
+    {
+      printf ("%s: using %s\n", block_type_name (role), block_name (block));
+      block_set_role (role, block);
+    }
+}
+#endif
diff --git a/src/threads/init.h b/src/threads/init.h
new file mode 100644
index 0000000..8a3df90
--- /dev/null
+++ b/src/threads/init.h
@@ -0,0 +1,12 @@
+#ifndef THREADS_INIT_H
+#define THREADS_INIT_H
+
+#include <debug.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
+
+/* Page directory with kernel mappings only. */
+extern uint32_t *init_page_dir;
+
+#endif /* threads/init.h */
diff --git a/src/threads/interrupt.c b/src/threads/interrupt.c
new file mode 100644
index 0000000..e3b90dc
--- /dev/null
+++ b/src/threads/interrupt.c
@@ -0,0 +1,438 @@
+#include "threads/interrupt.h"
+#include <debug.h>
+#include <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+#include "threads/flags.h"
+#include "threads/intr-stubs.h"
+#include "threads/io.h"
+#include "threads/thread.h"
+#include "threads/vaddr.h"
+#include "devices/timer.h"
+
+/* Programmable Interrupt Controller (PIC) registers.
+   A PC has two PICs, called the master and slave PICs, with the
+   slave attached ("cascaded") to the master IRQ line 2. */
+#define PIC0_CTRL	0x20    /* Master PIC control register address. */
+#define PIC0_DATA	0x21    /* Master PIC data register address. */
+#define PIC1_CTRL	0xa0    /* Slave PIC control register address. */
+#define PIC1_DATA	0xa1    /* Slave PIC data register address. */
+
+/* Number of x86 interrupts. */
+#define INTR_CNT 256
+
+/* The Interrupt Descriptor Table (IDT).  The format is fixed by
+   the CPU.  See [IA32-v3a] sections 5.10 "Interrupt Descriptor
+   Table (IDT)", 5.11 "IDT Descriptors", 5.12.1.2 "Flag Usage By
+   Exception- or Interrupt-Handler Procedure". */
+static uint64_t idt[INTR_CNT];
+
+/* Interrupt handler functions for each interrupt. */
+static intr_handler_func *intr_handlers[INTR_CNT];
+
+/* Names for each interrupt, for debugging purposes. */
+static const char *intr_names[INTR_CNT];
+
+/* Number of unexpected interrupts for each vector.  An
+   unexpected interrupt is one that has no registered handler. */
+static unsigned int unexpected_cnt[INTR_CNT];
+
+/* External interrupts are those generated by devices outside the
+   CPU, such as the timer.  External interrupts run with
+   interrupts turned off, so they never nest, nor are they ever
+   pre-empted.  Handlers for external interrupts also may not
+   sleep, although they may invoke intr_yield_on_return() to
+   request that a new process be scheduled just before the
+   interrupt returns. */
+static bool in_external_intr;   /* Are we processing an external interrupt? */
+static bool yield_on_return;    /* Should we yield on interrupt return? */
+
+/* Programmable Interrupt Controller helpers. */
+static void pic_init (void);
+static void pic_end_of_interrupt (int irq);
+
+/* Interrupt Descriptor Table helpers. */
+static uint64_t make_intr_gate (void (*) (void), int dpl);
+static uint64_t make_trap_gate (void (*) (void), int dpl);
+static inline uint64_t make_idtr_operand (uint16_t limit, void *base);
+
+/* Interrupt handlers. */
+void intr_handler (struct intr_frame *args);
+static void unexpected_interrupt (const struct intr_frame *);
+
+/* Returns the current interrupt status. */
+enum intr_level
+intr_get_level (void) 
+{
+  uint32_t flags;
+
+  /* Push the flags register on the processor stack, then pop the
+     value off the stack into `flags'.  See [IA32-v2b] "PUSHF"
+     and "POP" and [IA32-v3a] 5.8.1 "Masking Maskable Hardware
+     Interrupts". */
+  asm volatile ("pushfl; popl %0" : "=g" (flags));
+
+  return flags & FLAG_IF ? INTR_ON : INTR_OFF;
+}
+
+/* Enables or disables interrupts as specified by LEVEL and
+   returns the previous interrupt status. */
+enum intr_level
+intr_set_level (enum intr_level level) 
+{
+  return level == INTR_ON ? intr_enable () : intr_disable ();
+}
+
+/* Enables interrupts and returns the previous interrupt status. */
+enum intr_level
+intr_enable (void) 
+{
+  enum intr_level old_level = intr_get_level ();
+  ASSERT (!intr_context ());
+
+  /* Enable interrupts by setting the interrupt flag.
+
+     See [IA32-v2b] "STI" and [IA32-v3a] 5.8.1 "Masking Maskable
+     Hardware Interrupts". */
+  asm volatile ("sti");
+
+  return old_level;
+}
+
+/* Disables interrupts and returns the previous interrupt status. */
+enum intr_level
+intr_disable (void) 
+{
+  enum intr_level old_level = intr_get_level ();
+
+  /* Disable interrupts by clearing the interrupt flag.
+     See [IA32-v2b] "CLI" and [IA32-v3a] 5.8.1 "Masking Maskable
+     Hardware Interrupts". */
+  asm volatile ("cli" : : : "memory");
+
+  return old_level;
+}
+
+/* Initializes the interrupt system. */
+void
+intr_init (void)
+{
+  uint64_t idtr_operand;
+  int i;
+
+  /* Initialize interrupt controller. */
+  pic_init ();
+
+  /* Initialize IDT. */
+  for (i = 0; i < INTR_CNT; i++)
+    idt[i] = make_intr_gate (intr_stubs[i], 0);
+
+  /* Load IDT register.
+     See [IA32-v2a] "LIDT" and [IA32-v3a] 5.10 "Interrupt
+     Descriptor Table (IDT)". */
+  idtr_operand = make_idtr_operand (sizeof idt - 1, idt);
+  asm volatile ("lidt %0" : : "m" (idtr_operand));
+
+  /* Initialize intr_names. */
+  for (i = 0; i < INTR_CNT; i++)
+    intr_names[i] = "unknown";
+  intr_names[0] = "#DE Divide Error";
+  intr_names[1] = "#DB Debug Exception";
+  intr_names[2] = "NMI Interrupt";
+  intr_names[3] = "#BP Breakpoint Exception";
+  intr_names[4] = "#OF Overflow Exception";
+  intr_names[5] = "#BR BOUND Range Exceeded Exception";
+  intr_names[6] = "#UD Invalid Opcode Exception";
+  intr_names[7] = "#NM Device Not Available Exception";
+  intr_names[8] = "#DF Double Fault Exception";
+  intr_names[9] = "Coprocessor Segment Overrun";
+  intr_names[10] = "#TS Invalid TSS Exception";
+  intr_names[11] = "#NP Segment Not Present";
+  intr_names[12] = "#SS Stack Fault Exception";
+  intr_names[13] = "#GP General Protection Exception";
+  intr_names[14] = "#PF Page-Fault Exception";
+  intr_names[16] = "#MF x87 FPU Floating-Point Error";
+  intr_names[17] = "#AC Alignment Check Exception";
+  intr_names[18] = "#MC Machine-Check Exception";
+  intr_names[19] = "#XF SIMD Floating-Point Exception";
+}
+
+/* Registers interrupt VEC_NO to invoke HANDLER with descriptor
+   privilege level DPL.  Names the interrupt NAME for debugging
+   purposes.  The interrupt handler will be invoked with
+   interrupt status set to LEVEL. */
+static void
+register_handler (uint8_t vec_no, int dpl, enum intr_level level,
+                  intr_handler_func *handler, const char *name)
+{
+  ASSERT (intr_handlers[vec_no] == NULL);
+  if (level == INTR_ON)
+    idt[vec_no] = make_trap_gate (intr_stubs[vec_no], dpl);
+  else
+    idt[vec_no] = make_intr_gate (intr_stubs[vec_no], dpl);
+  intr_handlers[vec_no] = handler;
+  intr_names[vec_no] = name;
+}
+
+/* Registers external interrupt VEC_NO to invoke HANDLER, which
+   is named NAME for debugging purposes.  The handler will
+   execute with interrupts disabled. */
+void
+intr_register_ext (uint8_t vec_no, intr_handler_func *handler,
+                   const char *name) 
+{
+  ASSERT (vec_no >= 0x20 && vec_no <= 0x2f);
+  register_handler (vec_no, 0, INTR_OFF, handler, name);
+}
+
+/* Registers internal interrupt VEC_NO to invoke HANDLER, which
+   is named NAME for debugging purposes.  The interrupt handler
+   will be invoked with interrupt status LEVEL.
+
+   The handler will have descriptor privilege level DPL, meaning
+   that it can be invoked intentionally when the processor is in
+   the DPL or lower-numbered ring.  In practice, DPL==3 allows
+   user mode to invoke the interrupts and DPL==0 prevents such
+   invocation.  Faults and exceptions that occur in user mode
+   still cause interrupts with DPL==0 to be invoked.  See
+   [IA32-v3a] sections 4.5 "Privilege Levels" and 4.8.1.1
+   "Accessing Nonconforming Code Segments" for further
+   discussion. */
+void
+intr_register_int (uint8_t vec_no, int dpl, enum intr_level level,
+                   intr_handler_func *handler, const char *name)
+{
+  ASSERT (vec_no < 0x20 || vec_no > 0x2f);
+  register_handler (vec_no, dpl, level, handler, name);
+}
+
+/* Returns true during processing of an external interrupt
+   and false at all other times. */
+bool
+intr_context (void) 
+{
+  return in_external_intr;
+}
+
+/* During processing of an external interrupt, directs the
+   interrupt handler to yield to a new process just before
+   returning from the interrupt.  May not be called at any other
+   time. */
+void
+intr_yield_on_return (void) 
+{
+  ASSERT (intr_context ());
+  yield_on_return = true;
+}
+
+/* 8259A Programmable Interrupt Controller. */
+
+/* Initializes the PICs.  Refer to [8259A] for details.
+
+   By default, interrupts 0...15 delivered by the PICs will go to
+   interrupt vectors 0...15.  Those vectors are also used for CPU
+   traps and exceptions, so we reprogram the PICs so that
+   interrupts 0...15 are delivered to interrupt vectors 32...47
+   (0x20...0x2f) instead. */
+static void
+pic_init (void)
+{
+  /* Mask all interrupts on both PICs. */
+  outb (PIC0_DATA, 0xff);
+  outb (PIC1_DATA, 0xff);
+
+  /* Initialize master. */
+  outb (PIC0_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
+  outb (PIC0_DATA, 0x20); /* ICW2: line IR0...7 -> irq 0x20...0x27. */
+  outb (PIC0_DATA, 0x04); /* ICW3: slave PIC on line IR2. */
+  outb (PIC0_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
+
+  /* Initialize slave. */
+  outb (PIC1_CTRL, 0x11); /* ICW1: single mode, edge triggered, expect ICW4. */
+  outb (PIC1_DATA, 0x28); /* ICW2: line IR0...7 -> irq 0x28...0x2f. */
+  outb (PIC1_DATA, 0x02); /* ICW3: slave ID is 2. */
+  outb (PIC1_DATA, 0x01); /* ICW4: 8086 mode, normal EOI, non-buffered. */
+
+  /* Unmask all interrupts. */
+  outb (PIC0_DATA, 0x00);
+  outb (PIC1_DATA, 0x00);
+}
+
+/* Sends an end-of-interrupt signal to the PIC for the given IRQ.
+   If we don't acknowledge the IRQ, it will never be delivered to
+   us again, so this is important.  */
+static void
+pic_end_of_interrupt (int irq) 
+{
+  ASSERT (irq >= 0x20 && irq < 0x30);
+
+  /* Acknowledge master PIC. */
+  outb (0x20, 0x20);
+
+  /* Acknowledge slave PIC if this is a slave interrupt. */
+  if (irq >= 0x28)
+    outb (0xa0, 0x20);
+}
+
+/* Creates an gate that invokes FUNCTION.
+
+   The gate has descriptor privilege level DPL, meaning that it
+   can be invoked intentionally when the processor is in the DPL
+   or lower-numbered ring.  In practice, DPL==3 allows user mode
+   to call into the gate and DPL==0 prevents such calls.  Faults
+   and exceptions that occur in user mode still cause gates with
+   DPL==0 to be invoked.  See [IA32-v3a] sections 4.5 "Privilege
+   Levels" and 4.8.1.1 "Accessing Nonconforming Code Segments"
+   for further discussion.
+
+   TYPE must be either 14 (for an interrupt gate) or 15 (for a
+   trap gate).  The difference is that entering an interrupt gate
+   disables interrupts, but entering a trap gate does not.  See
+   [IA32-v3a] section 5.12.1.2 "Flag Usage By Exception- or
+   Interrupt-Handler Procedure" for discussion. */
+static uint64_t
+make_gate (void (*function) (void), int dpl, int type)
+{
+  uint32_t e0, e1;
+
+  ASSERT (function != NULL);
+  ASSERT (dpl >= 0 && dpl <= 3);
+  ASSERT (type >= 0 && type <= 15);
+
+  e0 = (((uint32_t) function & 0xffff)     /* Offset 15:0. */
+        | (SEL_KCSEG << 16));              /* Target code segment. */
+
+  e1 = (((uint32_t) function & 0xffff0000) /* Offset 31:16. */
+        | (1 << 15)                        /* Present. */
+        | ((uint32_t) dpl << 13)           /* Descriptor privilege level. */
+        | (0 << 12)                        /* System. */
+        | ((uint32_t) type << 8));         /* Gate type. */
+
+  return e0 | ((uint64_t) e1 << 32);
+}
+
+/* Creates an interrupt gate that invokes FUNCTION with the given
+   DPL. */
+static uint64_t
+make_intr_gate (void (*function) (void), int dpl)
+{
+  return make_gate (function, dpl, 14);
+}
+
+/* Creates a trap gate that invokes FUNCTION with the given
+   DPL. */
+static uint64_t
+make_trap_gate (void (*function) (void), int dpl)
+{
+  return make_gate (function, dpl, 15);
+}
+
+/* Returns a descriptor that yields the given LIMIT and BASE when
+   used as an operand for the LIDT instruction. */
+static inline uint64_t
+make_idtr_operand (uint16_t limit, void *base)
+{
+  return limit | ((uint64_t) (uint32_t) base << 16);
+}
+
+/* Interrupt handlers. */
+
+/* Handler for all interrupts, faults, and exceptions.  This
+   function is called by the assembly language interrupt stubs in
+   intr-stubs.S.  FRAME describes the interrupt and the
+   interrupted thread's registers. */
+void
+intr_handler (struct intr_frame *frame) 
+{
+  bool external;
+  intr_handler_func *handler;
+
+  /* External interrupts are special.
+     We only handle one at a time (so interrupts must be off)
+     and they need to be acknowledged on the PIC (see below).
+     An external interrupt handler cannot sleep. */
+  external = frame->vec_no >= 0x20 && frame->vec_no < 0x30;
+  if (external) 
+    {
+      ASSERT (intr_get_level () == INTR_OFF);
+      ASSERT (!intr_context ());
+
+      in_external_intr = true;
+      yield_on_return = false;
+    }
+
+  /* Invoke the interrupt's handler. */
+  handler = intr_handlers[frame->vec_no];
+  if (handler != NULL)
+    handler (frame);
+  else if (frame->vec_no == 0x27 || frame->vec_no == 0x2f)
+    {
+      /* There is no handler, but this interrupt can trigger
+         spuriously due to a hardware fault or hardware race
+         condition.  Ignore it. */
+    }
+  else
+    unexpected_interrupt (frame);
+
+  /* Complete the processing of an external interrupt. */
+  if (external) 
+    {
+      ASSERT (intr_get_level () == INTR_OFF);
+      ASSERT (intr_context ());
+
+      in_external_intr = false;
+      pic_end_of_interrupt (frame->vec_no); 
+
+      if (yield_on_return) 
+        thread_yield (); 
+    }
+}
+
+/* Handles an unexpected interrupt with interrupt frame F.  An
+   unexpected interrupt is one that has no registered handler. */
+static void
+unexpected_interrupt (const struct intr_frame *f)
+{
+  /* Count the number so far. */
+  unsigned int n = ++unexpected_cnt[f->vec_no];
+
+  /* If the number is a power of 2, print a message.  This rate
+     limiting means that we get information about an uncommon
+     unexpected interrupt the first time and fairly often after
+     that, but one that occurs many times will not overwhelm the
+     console. */
+  if ((n & (n - 1)) == 0)
+    printf ("Unexpected interrupt %#04x (%s)\n",
+    f->vec_no, intr_names[f->vec_no]);
+}
+
+/* Dumps interrupt frame F to the console, for debugging. */
+void
+intr_dump_frame (const struct intr_frame *f) 
+{
+  uint32_t cr2;
+
+  /* Store current value of CR2 into `cr2'.
+     CR2 is the linear address of the last page fault.
+     See [IA32-v2a] "MOV--Move to/from Control Registers" and
+     [IA32-v3a] 5.14 "Interrupt 14--Page Fault Exception
+     (#PF)". */
+  asm ("movl %%cr2, %0" : "=r" (cr2));
+
+  printf ("Interrupt %#04x (%s) at eip=%p\n",
+          f->vec_no, intr_names[f->vec_no], f->eip);
+  printf (" cr2=%08"PRIx32" error=%08"PRIx32"\n", cr2, f->error_code);
+  printf (" eax=%08"PRIx32" ebx=%08"PRIx32" ecx=%08"PRIx32" edx=%08"PRIx32"\n",
+          f->eax, f->ebx, f->ecx, f->edx);
+  printf (" esi=%08"PRIx32" edi=%08"PRIx32" esp=%08"PRIx32" ebp=%08"PRIx32"\n",
+          f->esi, f->edi, (uint32_t) f->esp, f->ebp);
+  printf (" cs=%04"PRIx16" ds=%04"PRIx16" es=%04"PRIx16" ss=%04"PRIx16"\n",
+          f->cs, f->ds, f->es, f->ss);
+}
+
+/* Returns the name of interrupt VEC. */
+const char *
+intr_name (uint8_t vec) 
+{
+  return intr_names[vec];
+}
diff --git a/src/threads/interrupt.h b/src/threads/interrupt.h
new file mode 100644
index 0000000..d43e06d
--- /dev/null
+++ b/src/threads/interrupt.h
@@ -0,0 +1,70 @@
+#ifndef THREADS_INTERRUPT_H
+#define THREADS_INTERRUPT_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+/* Interrupts on or off? */
+enum intr_level 
+  {
+    INTR_OFF,             /* Interrupts disabled. */
+    INTR_ON               /* Interrupts enabled. */
+  };
+
+enum intr_level intr_get_level (void);
+enum intr_level intr_set_level (enum intr_level);
+enum intr_level intr_enable (void);
+enum intr_level intr_disable (void);
+
+/* Interrupt stack frame. */
+struct intr_frame
+  {
+    /* Pushed by intr_entry in intr-stubs.S.
+       These are the interrupted task's saved registers. */
+    uint32_t edi;               /* Saved EDI. */
+    uint32_t esi;               /* Saved ESI. */
+    uint32_t ebp;               /* Saved EBP. */
+    uint32_t esp_dummy;         /* Not used. */
+    uint32_t ebx;               /* Saved EBX. */
+    uint32_t edx;               /* Saved EDX. */
+    uint32_t ecx;               /* Saved ECX. */
+    uint32_t eax;               /* Saved EAX. */
+    uint16_t gs, :16;           /* Saved GS segment register. */
+    uint16_t fs, :16;           /* Saved FS segment register. */
+    uint16_t es, :16;           /* Saved ES segment register. */
+    uint16_t ds, :16;           /* Saved DS segment register. */
+
+    /* Pushed by intrNN_stub in intr-stubs.S. */
+    uint32_t vec_no;            /* Interrupt vector number. */
+
+    /* Sometimes pushed by the CPU,
+       otherwise for consistency pushed as 0 by intrNN_stub.
+       The CPU puts it just under `eip', but we move it here. */
+    uint32_t error_code;        /* Error code. */
+
+    /* Pushed by intrNN_stub in intr-stubs.S.
+       This frame pointer eases interpretation of backtraces. */
+    void *frame_pointer;        /* Saved EBP (frame pointer). */
+
+    /* Pushed by the CPU.
+       These are the interrupted task's saved registers. */
+    void (*eip) (void);         /* Next instruction to execute. */
+    uint16_t cs, :16;           /* Code segment for eip. */
+    uint32_t eflags;            /* Saved CPU flags. */
+    void *esp;                  /* Saved stack pointer. */
+    uint16_t ss, :16;           /* Data segment for esp. */
+  };
+
+typedef void intr_handler_func (struct intr_frame *);
+
+void intr_init (void);
+void intr_register_ext (uint8_t vec, intr_handler_func *, const char *name);
+void intr_register_int (uint8_t vec, int dpl, enum intr_level,
+                        intr_handler_func *, const char *name);
+bool intr_context (void);
+void intr_yield_on_return (void);
+
+void intr_dump_frame (const struct intr_frame *);
+const char *intr_name (uint8_t vec);
+
+#endif /* threads/interrupt.h */
diff --git a/src/threads/intr-stubs.S b/src/threads/intr-stubs.S
new file mode 100644
index 0000000..adb674e
--- /dev/null
+++ b/src/threads/intr-stubs.S
@@ -0,0 +1,203 @@
+#include "threads/loader.h"
+
+        .text
+
+/* Main interrupt entry point.
+
+   An internal or external interrupt starts in one of the
+   intrNN_stub routines, which push the `struct intr_frame'
+   frame_pointer, error_code, and vec_no members on the stack,
+   then jump here.
+
+   We save the rest of the `struct intr_frame' members to the
+   stack, set up some registers as needed by the kernel, and then
+   call intr_handler(), which actually handles the interrupt.
+
+   We "fall through" to intr_exit to return from the interrupt.
+*/
+.func intr_entry
+intr_entry:
+	/* Save caller's registers. */
+	pushl %ds
+	pushl %es
+	pushl %fs
+	pushl %gs
+	pushal
+        
+	/* Set up kernel environment. */
+	cld			/* String instructions go upward. */
+	mov $SEL_KDSEG, %eax	/* Initialize segment registers. */
+	mov %eax, %ds
+	mov %eax, %es
+	leal 56(%esp), %ebp	/* Set up frame pointer. */
+
+	/* Call interrupt handler. */
+	pushl %esp
+.globl intr_handler
+	call intr_handler
+	addl $4, %esp
+.endfunc
+
+/* Interrupt exit.
+
+   Restores the caller's registers, discards extra data on the
+   stack, and returns to the caller.
+
+   This is a separate function because it is called directly when
+   we launch a new user process (see start_process() in
+   userprog/process.c). */
+.globl intr_exit
+.func intr_exit
+intr_exit:
+        /* Restore caller's registers. */
+	popal
+	popl %gs
+	popl %fs
+	popl %es
+	popl %ds
+
+        /* Discard `struct intr_frame' vec_no, error_code,
+           frame_pointer members. */
+	addl $12, %esp
+
+        /* Return to caller. */
+	iret
+.endfunc
+
+/* Interrupt stubs.
+
+   This defines 256 fragments of code, named `intr00_stub'
+   through `intrff_stub', each of which is used as the entry
+   point for the corresponding interrupt vector.  It also puts
+   the address of each of these functions in the correct spot in
+   `intr_stubs', an array of function pointers.
+
+   Most of the stubs do this:
+
+        1. Push %ebp on the stack (frame_pointer in `struct intr_frame').
+
+        2. Push 0 on the stack (error_code).
+
+        3. Push the interrupt number on the stack (vec_no).
+
+   The CPU pushes an extra "error code" on the stack for a few
+   interrupts.  Because we want %ebp to be where the error code
+   is, we follow a different path:
+
+        1. Push a duplicate copy of the error code on the stack.
+
+        2. Replace the original copy of the error code by %ebp.
+
+        3. Push the interrupt number on the stack. */
+
+	.data
+.globl intr_stubs
+intr_stubs:
+
+/* This implements steps 1 and 2, described above, in the common
+   case where we just push a 0 error code. */
+#define zero                                    \
+	pushl %ebp;                             \
+	pushl $0
+
+/* This implements steps 1 and 2, described above, in the case
+   where the CPU already pushed an error code. */
+#define REAL                                    \
+        pushl (%esp);                           \
+        movl %ebp, 4(%esp)
+
+/* Emits a stub for interrupt vector NUMBER.
+   TYPE is `zero', for the case where we push a 0 error code,
+   or `REAL', if the CPU pushes an error code for us. */
+#define STUB(NUMBER, TYPE)                      \
+	.text;                                  \
+.func intr##NUMBER##_stub;			\
+intr##NUMBER##_stub:                            \
+	TYPE;                                   \
+	push $0x##NUMBER;                       \
+        jmp intr_entry;                         \
+.endfunc;					\
+                                                \
+	.data;                                  \
+	.long intr##NUMBER##_stub;
+
+/* All the stubs. */
+STUB(00, zero) STUB(01, zero) STUB(02, zero) STUB(03, zero)
+STUB(04, zero) STUB(05, zero) STUB(06, zero) STUB(07, zero)
+STUB(08, REAL) STUB(09, zero) STUB(0a, REAL) STUB(0b, REAL)
+STUB(0c, zero) STUB(0d, REAL) STUB(0e, REAL) STUB(0f, zero)
+
+STUB(10, zero) STUB(11, REAL) STUB(12, zero) STUB(13, zero)
+STUB(14, zero) STUB(15, zero) STUB(16, zero) STUB(17, zero)
+STUB(18, REAL) STUB(19, zero) STUB(1a, REAL) STUB(1b, REAL)
+STUB(1c, zero) STUB(1d, REAL) STUB(1e, REAL) STUB(1f, zero)
+
+STUB(20, zero) STUB(21, zero) STUB(22, zero) STUB(23, zero)
+STUB(24, zero) STUB(25, zero) STUB(26, zero) STUB(27, zero)
+STUB(28, zero) STUB(29, zero) STUB(2a, zero) STUB(2b, zero)
+STUB(2c, zero) STUB(2d, zero) STUB(2e, zero) STUB(2f, zero)
+
+STUB(30, zero) STUB(31, zero) STUB(32, zero) STUB(33, zero)
+STUB(34, zero) STUB(35, zero) STUB(36, zero) STUB(37, zero)
+STUB(38, zero) STUB(39, zero) STUB(3a, zero) STUB(3b, zero)
+STUB(3c, zero) STUB(3d, zero) STUB(3e, zero) STUB(3f, zero)
+
+STUB(40, zero) STUB(41, zero) STUB(42, zero) STUB(43, zero)
+STUB(44, zero) STUB(45, zero) STUB(46, zero) STUB(47, zero)
+STUB(48, zero) STUB(49, zero) STUB(4a, zero) STUB(4b, zero)
+STUB(4c, zero) STUB(4d, zero) STUB(4e, zero) STUB(4f, zero)
+
+STUB(50, zero) STUB(51, zero) STUB(52, zero) STUB(53, zero)
+STUB(54, zero) STUB(55, zero) STUB(56, zero) STUB(57, zero)
+STUB(58, zero) STUB(59, zero) STUB(5a, zero) STUB(5b, zero)
+STUB(5c, zero) STUB(5d, zero) STUB(5e, zero) STUB(5f, zero)
+
+STUB(60, zero) STUB(61, zero) STUB(62, zero) STUB(63, zero)
+STUB(64, zero) STUB(65, zero) STUB(66, zero) STUB(67, zero)
+STUB(68, zero) STUB(69, zero) STUB(6a, zero) STUB(6b, zero)
+STUB(6c, zero) STUB(6d, zero) STUB(6e, zero) STUB(6f, zero)
+
+STUB(70, zero) STUB(71, zero) STUB(72, zero) STUB(73, zero)
+STUB(74, zero) STUB(75, zero) STUB(76, zero) STUB(77, zero)
+STUB(78, zero) STUB(79, zero) STUB(7a, zero) STUB(7b, zero)
+STUB(7c, zero) STUB(7d, zero) STUB(7e, zero) STUB(7f, zero)
+
+STUB(80, zero) STUB(81, zero) STUB(82, zero) STUB(83, zero)
+STUB(84, zero) STUB(85, zero) STUB(86, zero) STUB(87, zero)
+STUB(88, zero) STUB(89, zero) STUB(8a, zero) STUB(8b, zero)
+STUB(8c, zero) STUB(8d, zero) STUB(8e, zero) STUB(8f, zero)
+
+STUB(90, zero) STUB(91, zero) STUB(92, zero) STUB(93, zero)
+STUB(94, zero) STUB(95, zero) STUB(96, zero) STUB(97, zero)
+STUB(98, zero) STUB(99, zero) STUB(9a, zero) STUB(9b, zero)
+STUB(9c, zero) STUB(9d, zero) STUB(9e, zero) STUB(9f, zero)
+
+STUB(a0, zero) STUB(a1, zero) STUB(a2, zero) STUB(a3, zero)
+STUB(a4, zero) STUB(a5, zero) STUB(a6, zero) STUB(a7, zero)
+STUB(a8, zero) STUB(a9, zero) STUB(aa, zero) STUB(ab, zero)
+STUB(ac, zero) STUB(ad, zero) STUB(ae, zero) STUB(af, zero)
+
+STUB(b0, zero) STUB(b1, zero) STUB(b2, zero) STUB(b3, zero)
+STUB(b4, zero) STUB(b5, zero) STUB(b6, zero) STUB(b7, zero)
+STUB(b8, zero) STUB(b9, zero) STUB(ba, zero) STUB(bb, zero)
+STUB(bc, zero) STUB(bd, zero) STUB(be, zero) STUB(bf, zero)
+
+STUB(c0, zero) STUB(c1, zero) STUB(c2, zero) STUB(c3, zero)
+STUB(c4, zero) STUB(c5, zero) STUB(c6, zero) STUB(c7, zero)
+STUB(c8, zero) STUB(c9, zero) STUB(ca, zero) STUB(cb, zero)
+STUB(cc, zero) STUB(cd, zero) STUB(ce, zero) STUB(cf, zero)
+
+STUB(d0, zero) STUB(d1, zero) STUB(d2, zero) STUB(d3, zero)
+STUB(d4, zero) STUB(d5, zero) STUB(d6, zero) STUB(d7, zero)
+STUB(d8, zero) STUB(d9, zero) STUB(da, zero) STUB(db, zero)
+STUB(dc, zero) STUB(dd, zero) STUB(de, zero) STUB(df, zero)
+
+STUB(e0, zero) STUB(e1, zero) STUB(e2, zero) STUB(e3, zero)
+STUB(e4, zero) STUB(e5, zero) STUB(e6, zero) STUB(e7, zero)
+STUB(e8, zero) STUB(e9, zero) STUB(ea, zero) STUB(eb, zero)
+STUB(ec, zero) STUB(ed, zero) STUB(ee, zero) STUB(ef, zero)
+
+STUB(f0, zero) STUB(f1, zero) STUB(f2, zero) STUB(f3, zero)
+STUB(f4, zero) STUB(f5, zero) STUB(f6, zero) STUB(f7, zero)
+STUB(f8, zero) STUB(f9, zero) STUB(fa, zero) STUB(fb, zero)
+STUB(fc, zero) STUB(fd, zero) STUB(fe, zero) STUB(ff, zero)
diff --git a/src/threads/intr-stubs.h b/src/threads/intr-stubs.h
new file mode 100644
index 0000000..9ceba15
--- /dev/null
+++ b/src/threads/intr-stubs.h
@@ -0,0 +1,19 @@
+#ifndef THREADS_INTR_STUBS_H
+#define THREADS_INTR_STUBS_H
+
+/* Interrupt stubs.
+
+   These are little snippets of code in intr-stubs.S, one for
+   each of the 256 possible x86 interrupts.  Each one does a
+   little bit of stack manipulation, then jumps to intr_entry().
+   See intr-stubs.S for more information.
+
+   This array points to each of the interrupt stub entry points
+   so that intr_init() can easily find them. */
+typedef void intr_stub_func (void);
+extern intr_stub_func *intr_stubs[256];
+
+/* Interrupt return path. */
+void intr_exit (void);
+
+#endif /* threads/intr-stubs.h */
diff --git a/src/threads/io.h b/src/threads/io.h
new file mode 100644
index 0000000..30d52da
--- /dev/null
+++ b/src/threads/io.h
@@ -0,0 +1,115 @@
+#ifndef THREADS_IO_H
+#define THREADS_IO_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+/* Reads and returns a byte from PORT. */
+static inline uint8_t
+inb (uint16_t port)
+{
+  /* See [IA32-v2a] "IN". */
+  uint8_t data;
+  asm volatile ("inb %w1, %b0" : "=a" (data) : "Nd" (port));
+  return data;
+}
+
+/* Reads CNT bytes from PORT, one after another, and stores them
+   into the buffer starting at ADDR. */
+static inline void
+insb (uint16_t port, void *addr, size_t cnt)
+{
+  /* See [IA32-v2a] "INS". */
+  asm volatile ("rep insb" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory");
+}
+
+/* Reads and returns 16 bits from PORT. */
+static inline uint16_t
+inw (uint16_t port)
+{
+  uint16_t data;
+  /* See [IA32-v2a] "IN". */
+  asm volatile ("inw %w1, %w0" : "=a" (data) : "Nd" (port));
+  return data;
+}
+
+/* Reads CNT 16-bit (halfword) units from PORT, one after
+   another, and stores them into the buffer starting at ADDR. */
+static inline void
+insw (uint16_t port, void *addr, size_t cnt)
+{
+  /* See [IA32-v2a] "INS". */
+  asm volatile ("rep insw" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory");
+}
+
+/* Reads and returns 32 bits from PORT. */
+static inline uint32_t
+inl (uint16_t port)
+{
+  /* See [IA32-v2a] "IN". */
+  uint32_t data;
+  asm volatile ("inl %w1, %0" : "=a" (data) : "Nd" (port));
+  return data;
+}
+
+/* Reads CNT 32-bit (word) units from PORT, one after another,
+   and stores them into the buffer starting at ADDR. */
+static inline void
+insl (uint16_t port, void *addr, size_t cnt)
+{
+  /* See [IA32-v2a] "INS". */
+  asm volatile ("rep insl" : "+D" (addr), "+c" (cnt) : "d" (port) : "memory");
+}
+
+/* Writes byte DATA to PORT. */
+static inline void
+outb (uint16_t port, uint8_t data)
+{
+  /* See [IA32-v2b] "OUT". */
+  asm volatile ("outb %b0, %w1" : : "a" (data), "Nd" (port));
+}
+
+/* Writes to PORT each byte of data in the CNT-byte buffer
+   starting at ADDR. */
+static inline void
+outsb (uint16_t port, const void *addr, size_t cnt)
+{
+  /* See [IA32-v2b] "OUTS". */
+  asm volatile ("rep outsb" : "+S" (addr), "+c" (cnt) : "d" (port));
+}
+
+/* Writes the 16-bit DATA to PORT. */
+static inline void
+outw (uint16_t port, uint16_t data)
+{
+  /* See [IA32-v2b] "OUT". */
+  asm volatile ("outw %w0, %w1" : : "a" (data), "Nd" (port));
+}
+
+/* Writes to PORT each 16-bit unit (halfword) of data in the
+   CNT-halfword buffer starting at ADDR. */
+static inline void
+outsw (uint16_t port, const void *addr, size_t cnt)
+{
+  /* See [IA32-v2b] "OUTS". */
+  asm volatile ("rep outsw" : "+S" (addr), "+c" (cnt) : "d" (port));
+}
+
+/* Writes the 32-bit DATA to PORT. */
+static inline void
+outl (uint16_t port, uint32_t data)
+{
+  /* See [IA32-v2b] "OUT". */
+  asm volatile ("outl %0, %w1" : : "a" (data), "Nd" (port));
+}
+
+/* Writes to PORT each 32-bit unit (word) of data in the CNT-word
+   buffer starting at ADDR. */
+static inline void
+outsl (uint16_t port, const void *addr, size_t cnt)
+{
+  /* See [IA32-v2b] "OUTS". */
+  asm volatile ("rep outsl" : "+S" (addr), "+c" (cnt) : "d" (port));
+}
+
+#endif /* threads/io.h */
diff --git a/src/threads/kernel.lds.S b/src/threads/kernel.lds.S
new file mode 100644
index 0000000..4840202
--- /dev/null
+++ b/src/threads/kernel.lds.S
@@ -0,0 +1,33 @@
+#include "threads/loader.h"
+
+OUTPUT_FORMAT("elf32-i386")
+OUTPUT_ARCH("i386")
+ENTRY(start)			/* Kernel starts at "start" symbol. */
+SECTIONS
+{
+  /* Specify the kernel base address. */
+  _start = LOADER_PHYS_BASE + LOADER_KERN_BASE;
+
+  /* Make room for the ELF headers. */
+  . = _start + SIZEOF_HEADERS;
+
+  /* Kernel starts with code, followed by read-only data and writable data. */
+  .text : { *(.start) *(.text) } = 0x90
+  .rodata : { *(.rodata) *(.rodata.*) 
+	      . = ALIGN(0x1000); 
+	      _end_kernel_text = .; }
+  .eh_frame : { *(.eh_frame) }
+  .data : { *(.data) 
+	    _signature = .; LONG(0xaa55aa55) }
+
+  .plt : { *(.plt*) }
+
+  /* BSS (zero-initialized data) is after everything else. */
+  _start_bss = .;
+  .bss : { *(.bss) }
+  _end_bss = .;
+
+  _end = .;
+
+  ASSERT (_end - _start <= 512K, "Kernel image is too big.")
+}
diff --git a/src/threads/loader.S b/src/threads/loader.S
new file mode 100644
index 0000000..dd87ea1
--- /dev/null
+++ b/src/threads/loader.S
@@ -0,0 +1,263 @@
+#include "threads/loader.h"
+
+#### Kernel loader.
+
+#### This code should be stored in the first sector of a hard disk.
+#### When the BIOS runs, it loads this code at physical address
+#### 0x7c00-0x7e00 (512 bytes) and jumps to the beginning of it,
+#### in real mode.  The loader loads the kernel into memory and jumps
+#### to its entry point, which is the start function in start.S.
+####
+#### The BIOS passes in the drive that the loader was read from as
+#### DL, with floppy drives numbered 0x00, 0x01, ... and hard drives
+#### numbered 0x80, 0x81, ...  We want to support booting a kernel on
+#### a different drive from the loader, so we don't take advantage of
+#### this.
+
+# Runs in real mode, which is a 16-bit segment.
+	.code16
+
+# Set up segment registers.
+# Set stack to grow downward from 60 kB (after boot, the kernel
+# continues to use this stack for its initial thread).
+
+	sub %ax, %ax
+	mov %ax, %ds
+	mov %ax, %ss
+	mov $0xf000, %esp
+
+# Configure serial port so we can report progress without connected VGA.
+# See [IntrList] for details.
+	sub %dx, %dx			# Serial port 0.
+	mov $0xe3, %al			# 9600 bps, N-8-1.
+					# AH is already 0 (Initialize Port).
+	int $0x14			# Destroys AX.
+
+	call puts
+	.string "PiLo"
+
+#### Read the partition table on each system hard disk and scan for a
+#### partition of type 0x20, which is the type that we use for a
+#### Pintos kernel.
+####
+#### Read [Partitions] for a description of the partition table format
+#### that we parse.
+####
+#### We print out status messages to show the disk and partition being
+#### scanned, e.g. hda1234 as we scan four partitions on the first
+#### hard disk.
+
+	mov $0x80, %dl			# Hard disk 0.
+read_mbr:
+	sub %ebx, %ebx			# Sector 0.
+	mov $0x2000, %ax		# Use 0x20000 for buffer.
+	mov %ax, %es
+	call read_sector
+	jc no_such_drive
+
+	# Print hd[a-z].
+	call puts
+	.string " hd"
+	mov %dl, %al
+	add $'a' - 0x80, %al
+	call putc
+
+	# Check for MBR signature--if not present, it's not a
+	# partitioned hard disk.
+	cmpw $0xaa55, %es:510
+	jne next_drive
+
+	mov $446, %si			# Offset of partition table entry 1.
+	mov $'1', %al
+check_partition:
+	# Is it an unused partition?
+	cmpl $0, %es:(%si)
+	je next_partition
+
+	# Print [1-4].
+	call putc
+
+	# Is it a Pintos kernel partition?
+	cmpb $0x20, %es:4(%si)
+	jne next_partition
+
+	# Is it a bootable partition?
+	cmpb $0x80, %es:(%si)
+	je load_kernel
+
+next_partition:
+	# No match for this partition, go on to the next one.
+	add $16, %si			# Offset to next partition table entry.
+	inc %al
+	cmp $510, %si
+	jb check_partition
+
+next_drive:
+	# No match on this drive, go on to the next one.
+	inc %dl
+	jnc read_mbr
+
+no_such_drive:
+no_boot_partition:
+	# Didn't find a Pintos kernel partition anywhere, give up.
+	call puts
+	.string "\rNot found\r"
+
+	# Notify BIOS that boot failed.  See [IntrList].
+	int $0x18
+
+#### We found a kernel.  The kernel's drive is in DL.  The partition
+#### table entry for the kernel's partition is at ES:SI.  Our job now
+#### is to read the kernel from disk and jump to its start address.
+
+load_kernel:
+	call puts
+	.string "\rLoading"
+
+	# Figure out number of sectors to read.  A Pintos kernel is
+	# just an ELF format object, which doesn't have an
+	# easy-to-read field to identify its own size (see [ELF1]).
+	# But we limit Pintos kernels to 512 kB for other reasons, so
+	# it's easy enough to just read the entire contents of the
+	# partition or 512 kB from disk, whichever is smaller.
+	mov %es:12(%si), %ecx		# EBP = number of sectors
+	cmp $1024, %ecx			# Cap size at 512 kB
+	jbe 1f
+	mov $1024, %cx
+1:
+
+	mov %es:8(%si), %ebx		# EBX = first sector
+	mov $0x2000, %ax		# Start load address: 0x20000
+
+next_sector:
+	# Read one sector into memory.
+	mov %ax, %es			# ES:0000 -> load address
+	call read_sector
+	jc read_failed
+
+	# Print '.' as progress indicator once every 16 sectors == 8 kB.
+	test $15, %bl
+	jnz 1f
+	call puts
+	.string "."
+1:
+
+	# Advance memory pointer and disk sector.
+	add $0x20, %ax
+	inc %bx
+	loop next_sector
+
+	call puts
+	.string "\r"
+
+#### Transfer control to the kernel that we loaded.  We read the start
+#### address out of the ELF header (see [ELF1]) and convert it from a
+#### 32-bit linear address into a 16:16 segment:offset address for
+#### real mode, then jump to the converted address.  The 80x86 doesn't
+#### have an instruction to jump to an absolute segment:offset kept in
+#### registers, so in fact we store the address in a temporary memory
+#### location, then jump indirectly through that location.  To save 4
+#### bytes in the loader, we reuse 4 bytes of the loader's code for
+#### this temporary pointer.
+
+	mov $0x2000, %ax
+	mov %ax, %es
+	mov %es:0x18, %dx
+	mov %dx, start
+	movw $0x2000, start + 2
+	ljmp *start
+
+read_failed:
+start:
+	# Disk sector read failed.
+	call puts
+1:	.string "\rBad read\r"
+
+	# Notify BIOS that boot failed.  See [IntrList].
+	int $0x18
+
+#### Print string subroutine.  To save space in the loader, this
+#### subroutine takes its null-terminated string argument from the
+#### code stream just after the call, and then returns to the byte
+#### just after the terminating null.  This subroutine preserves all
+#### general-purpose registers.
+
+puts:	xchg %si, %ss:(%esp)
+	push %ax
+next_char:
+	mov %cs:(%si), %al
+	inc %si
+	test %al, %al
+	jz 1f
+	call putc
+	jmp next_char
+1:	pop %ax
+	xchg %si, %ss:(%esp)
+	ret
+
+#### Character output subroutine.  Prints the character in AL to the
+#### VGA display and serial port 0, using BIOS services (see
+#### [IntrList]).  Preserves all general-purpose registers.
+####
+#### If called upon to output a carriage return, this subroutine
+#### automatically supplies the following line feed.
+
+putc:	pusha
+
+1:	sub %bh, %bh			# Page 0.
+	mov $0x0e, %ah			# Teletype output service.
+	int $0x10
+
+	mov $0x01, %ah			# Serial port output service.
+	sub %dx, %dx			# Serial port 0.
+2:	int $0x14			# Destroys AH.
+	test $0x80, %ah			# Output timed out?
+	jz 3f
+	movw $0x9090, 2b		# Turn "int $0x14" above into NOPs.
+
+3:
+	cmp $'\r', %al
+	jne popa_ret
+	mov $'\n', %al
+	jmp 1b
+
+#### Sector read subroutine.  Takes a drive number in DL (0x80 = hard
+#### disk 0, 0x81 = hard disk 1, ...) and a sector number in EBX, and
+#### reads the specified sector into memory at ES:0000.  Returns with
+#### carry set on error, clear otherwise.  Preserves all
+#### general-purpose registers.
+
+read_sector:
+	pusha
+	sub %ax, %ax
+	push %ax			# LBA sector number [48:63]
+	push %ax			# LBA sector number [32:47]
+	push %ebx			# LBA sector number [0:31]
+	push %es			# Buffer segment
+	push %ax			# Buffer offset (always 0)
+	push $1				# Number of sectors to read
+	push $16			# Packet size
+	mov $0x42, %ah			# Extended read
+	mov %sp, %si			# DS:SI -> packet
+	int $0x13			# Error code in CF
+	popa				# Pop 16 bytes, preserve flags
+popa_ret:
+	popa
+	ret				# Error code still in CF
+
+#### Command-line arguments and their count.
+#### This is written by the `pintos' utility and read by the kernel.
+#### The loader itself does not do anything with the command line.
+	.org LOADER_ARG_CNT - LOADER_BASE
+	.fill LOADER_ARG_CNT_LEN, 1, 0
+
+	.org LOADER_ARGS - LOADER_BASE
+	.fill LOADER_ARGS_LEN, 1, 0
+
+#### Partition table.
+	.org LOADER_PARTS - LOADER_BASE
+	.fill LOADER_PARTS_LEN, 1, 0
+
+#### Boot-sector signature for BIOS inspection.
+	.org LOADER_SIG - LOADER_BASE
+	.word 0xaa55
diff --git a/src/threads/loader.h b/src/threads/loader.h
new file mode 100644
index 0000000..1bfe111
--- /dev/null
+++ b/src/threads/loader.h
@@ -0,0 +1,40 @@
+#ifndef THREADS_LOADER_H
+#define THREADS_LOADER_H
+
+/* Constants fixed by the PC BIOS. */
+#define LOADER_BASE 0x7c00      /* Physical address of loader's base. */
+#define LOADER_END  0x7e00      /* Physical address of end of loader. */
+
+/* Physical address of kernel base. */
+#define LOADER_KERN_BASE 0x20000       /* 128 kB. */
+
+/* Kernel virtual address at which all physical memory is mapped.
+   Must be aligned on a 4 MB boundary. */
+#define LOADER_PHYS_BASE 0xc0000000     /* 3 GB. */
+
+/* Important loader physical addresses. */
+#define LOADER_SIG (LOADER_END - LOADER_SIG_LEN)   /* 0xaa55 BIOS signature. */
+#define LOADER_PARTS (LOADER_SIG - LOADER_PARTS_LEN)     /* Partition table. */
+#define LOADER_ARGS (LOADER_PARTS - LOADER_ARGS_LEN)   /* Command-line args. */
+#define LOADER_ARG_CNT (LOADER_ARGS - LOADER_ARG_CNT_LEN) /* Number of args. */
+
+/* Sizes of loader data structures. */
+#define LOADER_SIG_LEN 2
+#define LOADER_PARTS_LEN 64
+#define LOADER_ARGS_LEN 128
+#define LOADER_ARG_CNT_LEN 4
+
+/* GDT selectors defined by loader.
+   More selectors are defined by userprog/gdt.h. */
+#define SEL_NULL        0x00    /* Null selector. */
+#define SEL_KCSEG       0x08    /* Kernel code selector. */
+#define SEL_KDSEG       0x10    /* Kernel data selector. */
+
+#ifndef __ASSEMBLER__
+#include <stdint.h>
+
+/* Amount of physical memory, in 4 kB pages. */
+extern uint32_t init_ram_pages;
+#endif
+
+#endif /* threads/loader.h */
diff --git a/src/threads/malloc.c b/src/threads/malloc.c
new file mode 100644
index 0000000..f6f803b
--- /dev/null
+++ b/src/threads/malloc.c
@@ -0,0 +1,294 @@
+#include "threads/malloc.h"
+#include <debug.h>
+#include <list.h>
+#include <round.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include "threads/palloc.h"
+#include "threads/synch.h"
+#include "threads/vaddr.h"
+
+/* A simple implementation of malloc().
+
+   The size of each request, in bytes, is rounded up to a power
+   of 2 and assigned to the "descriptor" that manages blocks of
+   that size.  The descriptor keeps a list of free blocks.  If
+   the free list is nonempty, one of its blocks is used to
+   satisfy the request.
+
+   Otherwise, a new page of memory, called an "arena", is
+   obtained from the page allocator (if none is available,
+   malloc() returns a null pointer).  The new arena is divided
+   into blocks, all of which are added to the descriptor's free
+   list.  Then we return one of the new blocks.
+
+   When we free a block, we add it to its descriptor's free list.
+   But if the arena that the block was in now has no in-use
+   blocks, we remove all of the arena's blocks from the free list
+   and give the arena back to the page allocator.
+
+   We can't handle blocks bigger than 2 kB using this scheme,
+   because they're too big to fit in a single page with a
+   descriptor.  We handle those by allocating contiguous pages
+   with the page allocator and sticking the allocation size at
+   the beginning of the allocated block's arena header. */
+
+/* Descriptor. */
+struct desc
+  {
+    size_t block_size;          /* Size of each element in bytes. */
+    size_t blocks_per_arena;    /* Number of blocks in an arena. */
+    struct list free_list;      /* List of free blocks. */
+    struct lock lock;           /* Lock. */
+  };
+
+/* Magic number for detecting arena corruption. */
+#define ARENA_MAGIC 0x9a548eed
+
+/* Arena. */
+struct arena 
+  {
+    unsigned magic;             /* Always set to ARENA_MAGIC. */
+    struct desc *desc;          /* Owning descriptor, null for big block. */
+    size_t free_cnt;            /* Free blocks; pages in big block. */
+  };
+
+/* Free block. */
+struct block 
+  {
+    struct list_elem free_elem; /* Free list element. */
+  };
+
+/* Our set of descriptors. */
+static struct desc descs[10];   /* Descriptors. */
+static size_t desc_cnt;         /* Number of descriptors. */
+
+static struct arena *block_to_arena (struct block *);
+static struct block *arena_to_block (struct arena *, size_t idx);
+
+/* Initializes the malloc() descriptors. */
+void
+malloc_init (void) 
+{
+  size_t block_size;
+
+  for (block_size = 16; block_size < PGSIZE / 2; block_size *= 2)
+    {
+      struct desc *d = &descs[desc_cnt++];
+      ASSERT (desc_cnt <= sizeof descs / sizeof *descs);
+      d->block_size = block_size;
+      d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size;
+      list_init (&d->free_list);
+      lock_init (&d->lock);
+    }
+}
+
+/* Obtains and returns a new block of at least SIZE bytes.
+   Returns a null pointer if memory is not available. */
+void *
+malloc (size_t size) 
+{
+  struct desc *d;
+  struct block *b;
+  struct arena *a;
+
+  /* A null pointer satisfies a request for 0 bytes. */
+  if (size == 0)
+    return NULL;
+
+  /* Find the smallest descriptor that satisfies a SIZE-byte
+     request. */
+  for (d = descs; d < descs + desc_cnt; d++)
+    if (d->block_size >= size)
+      break;
+  if (d == descs + desc_cnt) 
+    {
+      /* SIZE is too big for any descriptor.
+         Allocate enough pages to hold SIZE plus an arena. */
+      size_t page_cnt = DIV_ROUND_UP (size + sizeof *a, PGSIZE);
+      a = palloc_get_multiple (0, page_cnt);
+      if (a == NULL)
+        return NULL;
+
+      /* Initialize the arena to indicate a big block of PAGE_CNT
+         pages, and return it. */
+      a->magic = ARENA_MAGIC;
+      a->desc = NULL;
+      a->free_cnt = page_cnt;
+      return a + 1;
+    }
+
+  lock_acquire (&d->lock);
+
+  /* If the free list is empty, create a new arena. */
+  if (list_empty (&d->free_list))
+    {
+      size_t i;
+
+      /* Allocate a page. */
+      a = palloc_get_page (0);
+      if (a == NULL) 
+        {
+          lock_release (&d->lock);
+          return NULL; 
+        }
+
+      /* Initialize arena and add its blocks to the free list. */
+      a->magic = ARENA_MAGIC;
+      a->desc = d;
+      a->free_cnt = d->blocks_per_arena;
+      for (i = 0; i < d->blocks_per_arena; i++) 
+        {
+          struct block *b = arena_to_block (a, i);
+          list_push_back (&d->free_list, &b->free_elem);
+        }
+    }
+
+  /* Get a block from free list and return it. */
+  b = list_entry (list_pop_front (&d->free_list), struct block, free_elem);
+  a = block_to_arena (b);
+  a->free_cnt--;
+  lock_release (&d->lock);
+  return b;
+}
+
+/* Allocates and return A times B bytes initialized to zeroes.
+   Returns a null pointer if memory is not available. */
+void *
+calloc (size_t a, size_t b) 
+{
+  void *p;
+  size_t size;
+
+  /* Calculate block size and make sure it fits in size_t. */
+  size = a * b;
+  if (size < a || size < b)
+    return NULL;
+
+  /* Allocate and zero memory. */
+  p = malloc (size);
+  if (p != NULL)
+    memset (p, 0, size);
+
+  return p;
+}
+
+/* Returns the number of bytes allocated for BLOCK. */
+static size_t
+block_size (void *block) 
+{
+  struct block *b = block;
+  struct arena *a = block_to_arena (b);
+  struct desc *d = a->desc;
+
+  return d != NULL ? d->block_size : PGSIZE * a->free_cnt - pg_ofs (block);
+}
+
+/* Attempts to resize OLD_BLOCK to NEW_SIZE bytes, possibly
+   moving it in the process.
+   If successful, returns the new block; on failure, returns a
+   null pointer.
+   A call with null OLD_BLOCK is equivalent to malloc(NEW_SIZE).
+   A call with zero NEW_SIZE is equivalent to free(OLD_BLOCK). */
+void *
+realloc (void *old_block, size_t new_size) 
+{
+  if (new_size == 0) 
+    {
+      free (old_block);
+      return NULL;
+    }
+  else 
+    {
+      void *new_block = malloc (new_size);
+      if (old_block != NULL && new_block != NULL)
+        {
+          size_t old_size = block_size (old_block);
+          size_t min_size = new_size < old_size ? new_size : old_size;
+          memcpy (new_block, old_block, min_size);
+          free (old_block);
+        }
+      return new_block;
+    }
+}
+
+/* Frees block P, which must have been previously allocated with
+   malloc(), calloc(), or realloc(). */
+void
+free (void *p) 
+{
+  if (p != NULL)
+    {
+      struct block *b = p;
+      struct arena *a = block_to_arena (b);
+      struct desc *d = a->desc;
+      
+      if (d != NULL) 
+        {
+          /* It's a normal block.  We handle it here. */
+
+#ifndef NDEBUG
+          /* Clear the block to help detect use-after-free bugs. */
+          memset (b, 0xcc, d->block_size);
+#endif
+  
+          lock_acquire (&d->lock);
+
+          /* Add block to free list. */
+          list_push_front (&d->free_list, &b->free_elem);
+
+          /* If the arena is now entirely unused, free it. */
+          if (++a->free_cnt >= d->blocks_per_arena) 
+            {
+              size_t i;
+
+              ASSERT (a->free_cnt == d->blocks_per_arena);
+              for (i = 0; i < d->blocks_per_arena; i++) 
+                {
+                  struct block *b = arena_to_block (a, i);
+                  list_remove (&b->free_elem);
+                }
+              palloc_free_page (a);
+            }
+
+          lock_release (&d->lock);
+        }
+      else
+        {
+          /* It's a big block.  Free its pages. */
+          palloc_free_multiple (a, a->free_cnt);
+          return;
+        }
+    }
+}
+
+/* Returns the arena that block B is inside. */
+static struct arena *
+block_to_arena (struct block *b)
+{
+  struct arena *a = pg_round_down (b);
+
+  /* Check that the arena is valid. */
+  ASSERT (a != NULL);
+  ASSERT (a->magic == ARENA_MAGIC);
+
+  /* Check that the block is properly aligned for the arena. */
+  ASSERT (a->desc == NULL
+          || (pg_ofs (b) - sizeof *a) % a->desc->block_size == 0);
+  ASSERT (a->desc != NULL || pg_ofs (b) == sizeof *a);
+
+  return a;
+}
+
+/* Returns the (IDX - 1)'th block within arena A. */
+static struct block *
+arena_to_block (struct arena *a, size_t idx) 
+{
+  ASSERT (a != NULL);
+  ASSERT (a->magic == ARENA_MAGIC);
+  ASSERT (idx < a->desc->blocks_per_arena);
+  return (struct block *) ((uint8_t *) a
+                           + sizeof *a
+                           + idx * a->desc->block_size);
+}
diff --git a/src/threads/malloc.h b/src/threads/malloc.h
new file mode 100644
index 0000000..bc55d36
--- /dev/null
+++ b/src/threads/malloc.h
@@ -0,0 +1,13 @@
+#ifndef THREADS_MALLOC_H
+#define THREADS_MALLOC_H
+
+#include <debug.h>
+#include <stddef.h>
+
+void malloc_init (void);
+void *malloc (size_t) __attribute__ ((malloc));
+void *calloc (size_t, size_t) __attribute__ ((malloc));
+void *realloc (void *, size_t);
+void free (void *);
+
+#endif /* threads/malloc.h */
diff --git a/src/threads/palloc.c b/src/threads/palloc.c
new file mode 100644
index 0000000..4fc8394
--- /dev/null
+++ b/src/threads/palloc.c
@@ -0,0 +1,182 @@
+#include "threads/palloc.h"
+#include <bitmap.h>
+#include <debug.h>
+#include <inttypes.h>
+#include <round.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include "threads/loader.h"
+#include "threads/synch.h"
+#include "threads/vaddr.h"
+
+/* Page allocator.  Hands out memory in page-size (or
+   page-multiple) chunks.  See malloc.h for an allocator that
+   hands out smaller chunks.
+
+   System memory is divided into two "pools" called the kernel
+   and user pools.  The user pool is for user (virtual) memory
+   pages, the kernel pool for everything else.  The idea here is
+   that the kernel needs to have memory for its own operations
+   even if user processes are swapping like mad.
+
+   By default, half of system RAM is given to the kernel pool and
+   half to the user pool.  That should be huge overkill for the
+   kernel pool, but that's just fine for demonstration purposes. */
+
+/* A memory pool. */
+struct pool
+  {
+    struct lock lock;                   /* Mutual exclusion. */
+    struct bitmap *used_map;            /* Bitmap of free pages. */
+    uint8_t *base;                      /* Base of pool. */
+  };
+
+/* Two pools: one for kernel data, one for user pages. */
+static struct pool kernel_pool, user_pool;
+
+static void init_pool (struct pool *, void *base, size_t page_cnt,
+                       const char *name);
+static bool page_from_pool (const struct pool *, void *page);
+
+/* Initializes the page allocator.  At most USER_PAGE_LIMIT
+   pages are put into the user pool. */
+void
+palloc_init (size_t user_page_limit)
+{
+  /* Free memory starts at 1 MB and runs to the end of RAM. */
+  uint8_t *free_start = ptov (1024 * 1024);
+  uint8_t *free_end = ptov (init_ram_pages * PGSIZE);
+  size_t free_pages = (free_end - free_start) / PGSIZE;
+  size_t user_pages = free_pages / 2;
+  size_t kernel_pages;
+  if (user_pages > user_page_limit)
+    user_pages = user_page_limit;
+  kernel_pages = free_pages - user_pages;
+
+  /* Give half of memory to kernel, half to user. */
+  init_pool (&kernel_pool, free_start, kernel_pages, "kernel pool");
+  init_pool (&user_pool, free_start + kernel_pages * PGSIZE,
+             user_pages, "user pool");
+}
+
+/* Obtains and returns a group of PAGE_CNT contiguous free pages.
+   If PAL_USER is set, the pages are obtained from the user pool,
+   otherwise from the kernel pool.  If PAL_ZERO is set in FLAGS,
+   then the pages are filled with zeros.  If too few pages are
+   available, returns a null pointer, unless PAL_ASSERT is set in
+   FLAGS, in which case the kernel panics. */
+void *
+palloc_get_multiple (enum palloc_flags flags, size_t page_cnt)
+{
+  struct pool *pool = flags & PAL_USER ? &user_pool : &kernel_pool;
+  void *pages;
+  size_t page_idx;
+
+  if (page_cnt == 0)
+    return NULL;
+
+  lock_acquire (&pool->lock);
+  page_idx = bitmap_scan_and_flip (pool->used_map, 0, page_cnt, false);
+  lock_release (&pool->lock);
+
+  if (page_idx != BITMAP_ERROR)
+    pages = pool->base + PGSIZE * page_idx;
+  else
+    pages = NULL;
+
+  if (pages != NULL) 
+    {
+      if (flags & PAL_ZERO)
+        memset (pages, 0, PGSIZE * page_cnt);
+    }
+  else 
+    {
+      if (flags & PAL_ASSERT)
+        PANIC ("palloc_get: out of pages");
+    }
+
+  return pages;
+}
+
+/* Obtains a single free page and returns its kernel virtual
+   address.
+   If PAL_USER is set, the page is obtained from the user pool,
+   otherwise from the kernel pool.  If PAL_ZERO is set in FLAGS,
+   then the page is filled with zeros.  If no pages are
+   available, returns a null pointer, unless PAL_ASSERT is set in
+   FLAGS, in which case the kernel panics. */
+void *
+palloc_get_page (enum palloc_flags flags) 
+{
+  return palloc_get_multiple (flags, 1);
+}
+
+/* Frees the PAGE_CNT pages starting at PAGES. */
+void
+palloc_free_multiple (void *pages, size_t page_cnt) 
+{
+  struct pool *pool;
+  size_t page_idx;
+
+  ASSERT (pg_ofs (pages) == 0);
+  if (pages == NULL || page_cnt == 0)
+    return;
+
+  if (page_from_pool (&kernel_pool, pages))
+    pool = &kernel_pool;
+  else if (page_from_pool (&user_pool, pages))
+    pool = &user_pool;
+  else
+    NOT_REACHED ();
+
+  page_idx = pg_no (pages) - pg_no (pool->base);
+
+#ifndef NDEBUG
+  memset (pages, 0xcc, PGSIZE * page_cnt);
+#endif
+
+  ASSERT (bitmap_all (pool->used_map, page_idx, page_cnt));
+  bitmap_set_multiple (pool->used_map, page_idx, page_cnt, false);
+}
+
+/* Frees the page at PAGE. */
+void
+palloc_free_page (void *page) 
+{
+  palloc_free_multiple (page, 1);
+}
+
+/* Initializes pool P as starting at START and ending at END,
+   naming it NAME for debugging purposes. */
+static void
+init_pool (struct pool *p, void *base, size_t page_cnt, const char *name) 
+{
+  /* We'll put the pool's used_map at its base.
+     Calculate the space needed for the bitmap
+     and subtract it from the pool's size. */
+  size_t bm_pages = DIV_ROUND_UP (bitmap_buf_size (page_cnt), PGSIZE);
+  if (bm_pages > page_cnt)
+    PANIC ("Not enough memory in %s for bitmap.", name);
+  page_cnt -= bm_pages;
+
+  printf ("%zu pages available in %s.\n", page_cnt, name);
+
+  /* Initialize the pool. */
+  lock_init (&p->lock);
+  p->used_map = bitmap_create_in_buf (page_cnt, base, bm_pages * PGSIZE);
+  p->base = base + bm_pages * PGSIZE;
+}
+
+/* Returns true if PAGE was allocated from POOL,
+   false otherwise. */
+static bool
+page_from_pool (const struct pool *pool, void *page) 
+{
+  size_t page_no = pg_no (page);
+  size_t start_page = pg_no (pool->base);
+  size_t end_page = start_page + bitmap_size (pool->used_map);
+
+  return page_no >= start_page && page_no < end_page;
+}
diff --git a/src/threads/palloc.h b/src/threads/palloc.h
new file mode 100644
index 0000000..cb3b70e
--- /dev/null
+++ b/src/threads/palloc.h
@@ -0,0 +1,20 @@
+#ifndef THREADS_PALLOC_H
+#define THREADS_PALLOC_H
+
+#include <stddef.h>
+
+/* How to allocate pages. */
+enum palloc_flags
+  {
+    PAL_ASSERT = 001,           /* Panic on failure. */
+    PAL_ZERO = 002,             /* Zero page contents. */
+    PAL_USER = 004              /* User page. */
+  };
+
+void palloc_init (size_t user_page_limit);
+void *palloc_get_page (enum palloc_flags);
+void *palloc_get_multiple (enum palloc_flags, size_t page_cnt);
+void palloc_free_page (void *);
+void palloc_free_multiple (void *, size_t page_cnt);
+
+#endif /* threads/palloc.h */
diff --git a/src/threads/pte.h b/src/threads/pte.h
new file mode 100644
index 0000000..1660727
--- /dev/null
+++ b/src/threads/pte.h
@@ -0,0 +1,107 @@
+#ifndef THREADS_PTE_H
+#define THREADS_PTE_H
+
+#include "threads/vaddr.h"
+
+/* Functions and macros for working with x86 hardware page
+   tables.
+
+   See vaddr.h for more generic functions and macros for virtual
+   addresses.
+   
+   Virtual addresses are structured as follows:
+
+    31                  22 21                  12 11                   0
+   +----------------------+----------------------+----------------------+
+   | Page Directory Index |   Page Table Index   |    Page Offset       |
+   +----------------------+----------------------+----------------------+
+*/
+
+/* Page table index (bits 12:21). */
+#define	PTSHIFT PGBITS		           /* First page table bit. */
+#define PTBITS  10                         /* Number of page table bits. */
+#define PTSPAN  (1 << PTBITS << PGBITS)    /* Bytes covered by a page table. */
+#define PTMASK  BITMASK(PTSHIFT, PTBITS)   /* Page table bits (12:21). */
+
+/* Page directory index (bits 22:31). */
+#define PDSHIFT (PTSHIFT + PTBITS)         /* First page directory bit. */
+#define PDBITS  10                         /* Number of page dir bits. */
+#define PDMASK  BITMASK(PDSHIFT, PDBITS)   /* Page directory bits (22:31). */
+
+/* Obtains page table index from a virtual address. */
+static inline unsigned pt_no (const void *va) {
+  return ((uintptr_t) va & PTMASK) >> PTSHIFT;
+}
+
+/* Obtains page directory index from a virtual address. */
+static inline uintptr_t pd_no (const void *va) {
+  return (uintptr_t) va >> PDSHIFT;
+}
+
+/* Page directory and page table entries.
+
+   For more information see the section on page tables in the
+   Pintos reference guide chapter, or [IA32-v3a] 3.7.6
+   "Page-Directory and Page-Table Entries".
+
+   PDEs and PTEs share a common format:
+
+   31                                 12 11                     0
+   +------------------------------------+------------------------+
+   |         Physical Address           |         Flags          |
+   +------------------------------------+------------------------+
+
+   In a PDE, the physical address points to a page table.
+   In a PTE, the physical address points to a data or code page.
+   The important flags are listed below.
+   When a PDE or PTE is not "present", the other flags are
+   ignored.
+   A PDE or PTE that is initialized to 0 will be interpreted as
+   "not present", which is just fine. */
+#define PTE_FLAGS 0x00000fff    /* Flag bits. */
+#define PTE_ADDR  0xfffff000    /* Address bits. */
+#define PTE_AVL   0x00000e00    /* Bits available for OS use. */
+#define PTE_P 0x1               /* 1=present, 0=not present. */
+#define PTE_W 0x2               /* 1=read/write, 0=read-only. */
+#define PTE_U 0x4               /* 1=user/kernel, 0=kernel only. */
+#define PTE_A 0x20              /* 1=accessed, 0=not acccessed. */
+#define PTE_D 0x40              /* 1=dirty, 0=not dirty (PTEs only). */
+
+/* Returns a PDE that points to page table PT. */
+static inline uint32_t pde_create (uint32_t *pt) {
+  ASSERT (pg_ofs (pt) == 0);
+  return vtop (pt) | PTE_U | PTE_P | PTE_W;
+}
+
+/* Returns a pointer to the page table that page directory entry
+   PDE, which must "present", points to. */
+static inline uint32_t *pde_get_pt (uint32_t pde) {
+  ASSERT (pde & PTE_P);
+  return ptov (pde & PTE_ADDR);
+}
+
+/* Returns a PTE that points to PAGE.
+   The PTE's page is readable.
+   If WRITABLE is true then it will be writable as well.
+   The page will be usable only by ring 0 code (the kernel). */
+static inline uint32_t pte_create_kernel (void *page, bool writable) {
+  ASSERT (pg_ofs (page) == 0);
+  return vtop (page) | PTE_P | (writable ? PTE_W : 0);
+}
+
+/* Returns a PTE that points to PAGE.
+   The PTE's page is readable.
+   If WRITABLE is true then it will be writable as well.
+   The page will be usable by both user and kernel code. */
+static inline uint32_t pte_create_user (void *page, bool writable) {
+  return pte_create_kernel (page, writable) | PTE_U;
+}
+
+/* Returns a pointer to the page that page table entry PTE points
+   to. */
+static inline void *pte_get_page (uint32_t pte) {
+  return ptov (pte & PTE_ADDR);
+}
+
+#endif /* threads/pte.h */
+
diff --git a/src/threads/start.S b/src/threads/start.S
new file mode 100644
index 0000000..29ffa7a
--- /dev/null
+++ b/src/threads/start.S
@@ -0,0 +1,204 @@
+	#include "threads/loader.h"
+
+#### Kernel startup code.
+
+#### The loader (in loader.S) loads the kernel at physical address
+#### 0x20000 (128 kB) and jumps to "start", defined here.  This code
+#### switches from real mode to 32-bit protected mode and calls
+#### main().
+
+/* Flags in control register 0. */
+#define CR0_PE 0x00000001      /* Protection Enable. */
+#define CR0_EM 0x00000004      /* (Floating-point) Emulation. */
+#define CR0_PG 0x80000000      /* Paging. */
+#define CR0_WP 0x00010000      /* Write-Protect enable in kernel mode. */
+
+	.section .start
+
+# The following code runs in real mode, which is a 16-bit code segment.
+	.code16
+
+.func start
+.globl start
+start:
+
+# The loader called into us with CS = 0x2000, SS = 0x0000, ESP = 0xf000,
+# but we should initialize the other segment registers.
+
+	mov $0x2000, %ax
+	mov %ax, %ds
+	mov %ax, %es
+
+# Set string instructions to go upward.
+	cld
+
+#### Get memory size, via interrupt 15h function 88h (see [IntrList]),
+#### which returns AX = (kB of physical memory) - 1024.  This only
+#### works for memory sizes <= 65 MB, which should be fine for our
+#### purposes.  We cap memory at 64 MB because that's all we prepare
+#### page tables for, below.
+
+	movb $0x88, %ah
+	int $0x15
+	addl $1024, %eax	# Total kB memory
+	cmp $0x10000, %eax	# Cap at 64 MB
+	jbe 1f
+	mov $0x10000, %eax
+1:	shrl $2, %eax		# Total 4 kB pages
+	addr32 movl %eax, init_ram_pages - LOADER_PHYS_BASE - 0x20000
+
+#### Enable A20.  Address line 20 is tied low when the machine boots,
+#### which prevents addressing memory about 1 MB.  This code fixes it.
+
+# Poll status register while busy.
+
+1:	inb $0x64, %al
+	testb $0x2, %al
+	jnz 1b
+
+# Send command for writing output port.
+
+	movb $0xd1, %al
+	outb %al, $0x64
+
+# Poll status register while busy.
+
+1:	inb $0x64, %al
+	testb $0x2, %al
+	jnz 1b
+
+# Enable A20 line.
+
+	movb $0xdf, %al
+	outb %al, $0x60
+
+# Poll status register while busy.
+
+1:	inb $0x64, %al
+	testb $0x2, %al
+	jnz 1b
+
+#### Create temporary page directory and page table and set page
+#### directory base register.
+
+# Create page directory at 0xf000 (60 kB) and fill with zeroes.
+	mov $0xf00, %ax
+	mov %ax, %es
+	subl %eax, %eax
+	subl %edi, %edi
+	movl $0x400, %ecx
+	rep stosl
+
+# Add PDEs to point to page tables for the first 64 MB of RAM.
+# Also add identical PDEs starting at LOADER_PHYS_BASE.
+# See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries"
+# for a description of the bits in %eax.
+
+	movl $0x10007, %eax
+	movl $0x11, %ecx
+	subl %edi, %edi
+1:	movl %eax, %es:(%di)
+	movl %eax, %es:LOADER_PHYS_BASE >> 20(%di)
+	addw $4, %di
+	addl $0x1000, %eax
+	loop 1b
+
+# Set up page tables for one-to-map linear to physical map for the
+# first 64 MB of RAM.
+# See [IA32-v3a] section 3.7.6 "Page-Directory and Page-Table Entries"
+# for a description of the bits in %eax.
+
+	movw $0x1000, %ax
+	movw %ax, %es
+	movl $0x7, %eax
+	movl $0x4000, %ecx
+	subl %edi, %edi
+1:	movl %eax, %es:(%di)
+	addw $4, %di
+	addl $0x1000, %eax
+	loop 1b
+
+# Set page directory base register.
+
+	movl $0xf000, %eax
+	movl %eax, %cr3
+
+#### Switch to protected mode.
+
+# First, disable interrupts.  We won't set up the IDT until we get
+# into C code, so any interrupt would blow us away.
+
+	cli
+
+# Protected mode requires a GDT, so point the GDTR to our GDT.
+# We need a data32 prefix to ensure that all 32 bits of the GDT
+# descriptor are loaded (default is to load only 24 bits).
+# The CPU doesn't need an addr32 prefix but ELF doesn't do 16-bit
+# relocations.
+
+	data32 addr32 lgdt gdtdesc - LOADER_PHYS_BASE - 0x20000
+
+# Then we turn on the following bits in CR0:
+#    PE (Protect Enable): this turns on protected mode.
+#    PG (Paging): turns on paging.
+#    WP (Write Protect): if unset, ring 0 code ignores
+#       write-protect bits in page tables (!).
+#    EM (Emulation): forces floating-point instructions to trap.
+#       We don't support floating point.
+
+	movl %cr0, %eax
+	orl $CR0_PE | CR0_PG | CR0_WP | CR0_EM, %eax
+	movl %eax, %cr0
+
+# We're now in protected mode in a 16-bit segment.  The CPU still has
+# the real-mode code segment cached in %cs's segment descriptor.  We
+# need to reload %cs, and the easiest way is to use a far jump.
+# Because we're not running in a 32-bit segment the data32 prefix is
+# needed to jump to a 32-bit offset in the target segment.
+
+	data32 ljmp $SEL_KCSEG, $1f
+
+# We're now in protected mode in a 32-bit segment.
+# Let the assembler know.
+
+	.code32
+
+# Reload all the other segment registers and the stack pointer to
+# point into our new GDT.
+
+1:	mov $SEL_KDSEG, %ax
+	mov %ax, %ds
+	mov %ax, %es
+	mov %ax, %fs
+	mov %ax, %gs
+	mov %ax, %ss
+	addl $LOADER_PHYS_BASE, %esp
+	movl $0, %ebp			# Null-terminate main()'s backtrace
+
+#### Call main().
+
+	call main
+
+# main() shouldn't ever return.  If it does, spin.
+
+1:	jmp 1b
+.endfunc
+
+#### GDT
+
+	.align 8
+gdt:
+	.quad 0x0000000000000000	# Null segment.  Not used by CPU.
+	.quad 0x00cf9a000000ffff	# System code, base 0, limit 4 GB.
+	.quad 0x00cf92000000ffff        # System data, base 0, limit 4 GB.
+
+gdtdesc:
+	.word	gdtdesc - gdt - 1	# Size of the GDT, minus 1 byte.
+	.long	gdt			# Address of the GDT.
+
+#### Physical memory size in 4 kB pages.  This is exported to the rest
+#### of the kernel.
+.globl init_ram_pages
+init_ram_pages:
+	.long 0
+
diff --git a/src/threads/switch.S b/src/threads/switch.S
new file mode 100644
index 0000000..feca86c
--- /dev/null
+++ b/src/threads/switch.S
@@ -0,0 +1,65 @@
+#include "threads/switch.h"
+
+#### struct thread *switch_threads (struct thread *cur, struct thread *next);
+####
+#### Switches from CUR, which must be the running thread, to NEXT,
+#### which must also be running switch_threads(), returning CUR in
+#### NEXT's context.
+####
+#### This function works by assuming that the thread we're switching
+#### into is also running switch_threads().  Thus, all it has to do is
+#### preserve a few registers on the stack, then switch stacks and
+#### restore the registers.  As part of switching stacks we record the
+#### current stack pointer in CUR's thread structure.
+
+.globl switch_threads
+.func switch_threads
+switch_threads:
+	# Save caller's register state.
+	#
+	# Note that the SVR4 ABI allows us to destroy %eax, %ecx, %edx,
+	# but requires us to preserve %ebx, %ebp, %esi, %edi.  See
+	# [SysV-ABI-386] pages 3-11 and 3-12 for details.
+	#
+	# This stack frame must match the one set up by thread_create()
+	# in size.
+	pushl %ebx
+	pushl %ebp
+	pushl %esi
+	pushl %edi
+
+	# Get offsetof (struct thread, stack).
+.globl thread_stack_ofs
+	mov thread_stack_ofs, %edx
+
+	# Save current stack pointer to old thread's stack, if any.
+	movl SWITCH_CUR(%esp), %eax
+	movl %esp, (%eax,%edx,1)
+
+	# Restore stack pointer from new thread's stack.
+	movl SWITCH_NEXT(%esp), %ecx
+	movl (%ecx,%edx,1), %esp
+
+	# Restore caller's register state.
+	popl %edi
+	popl %esi
+	popl %ebp
+	popl %ebx
+        ret
+.endfunc
+
+.globl switch_entry
+.func switch_entry
+switch_entry:
+	# Discard switch_threads() arguments.
+	addl $8, %esp
+
+	# Call thread_schedule_tail(prev).
+	pushl %eax
+.globl thread_schedule_tail
+	call thread_schedule_tail
+	addl $4, %esp
+
+	# Start thread proper.
+	ret
+.endfunc
diff --git a/src/threads/switch.h b/src/threads/switch.h
new file mode 100644
index 0000000..cc156b6
--- /dev/null
+++ b/src/threads/switch.h
@@ -0,0 +1,39 @@
+#ifndef THREADS_SWITCH_H
+#define THREADS_SWITCH_H
+
+#ifndef __ASSEMBLER__
+/* switch_thread()'s stack frame. */
+struct switch_threads_frame 
+  {
+    uint32_t edi;               /*  0: Saved %edi. */
+    uint32_t esi;               /*  4: Saved %esi. */
+    uint32_t ebp;               /*  8: Saved %ebp. */
+    uint32_t ebx;               /* 12: Saved %ebx. */
+    void (*eip) (void);         /* 16: Return address. */
+    struct thread *cur;         /* 20: switch_threads()'s CUR argument. */
+    struct thread *next;        /* 24: switch_threads()'s NEXT argument. */
+  };
+
+/* Switches from CUR, which must be the running thread, to NEXT,
+   which must also be running switch_threads(), returning CUR in
+   NEXT's context. */
+struct thread *switch_threads (struct thread *cur, struct thread *next);
+
+/* Stack frame for switch_entry(). */
+struct switch_entry_frame
+  {
+    void (*eip) (void);
+  };
+
+void switch_entry (void);
+
+/* Pops the CUR and NEXT arguments off the stack, for use in
+   initializing threads. */
+void switch_thunk (void);
+#endif
+
+/* Offsets used by switch.S. */
+#define SWITCH_CUR      20
+#define SWITCH_NEXT     24
+
+#endif /* threads/switch.h */
diff --git a/src/threads/synch.c b/src/threads/synch.c
new file mode 100644
index 0000000..e92aab7
--- /dev/null
+++ b/src/threads/synch.c
@@ -0,0 +1,447 @@
+/* This file is derived from source code for the Nachos
+   instructional operating system.  The Nachos copyright notice
+   is reproduced in full below. */
+
+/* Copyright (c) 1992-1996 The Regents of the University of California.
+   All rights reserved.
+
+   Permission to use, copy, modify, and distribute this software
+   and its documentation for any purpose, without fee, and
+   without written agreement is hereby granted, provided that the
+   above copyright notice and the following two paragraphs appear
+   in all copies of this software.
+
+   IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO
+   ANY PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR
+   CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OF THIS SOFTWARE
+   AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA
+   HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+   THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY
+   WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+   WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+   PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
+   BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO
+   PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR
+   MODIFICATIONS.
+*/
+
+#include "threads/synch.h"
+#include <stdio.h>
+#include <string.h>
+#include "threads/interrupt.h"
+#include "threads/thread.h"
+
+/* Initializes semaphore SEMA to VALUE.  A semaphore is a
+   nonnegative integer along with two atomic operators for
+   manipulating it:
+
+   - down or "P": wait for the value to become positive, then
+     decrement it.
+
+   - up or "V": increment the value (and wake up one waiting
+     thread, if any). */
+void
+sema_init (struct semaphore *sema, unsigned value) 
+{
+  ASSERT (sema != NULL);
+
+  sema->value = value;
+  list_init (&sema->waiters);
+}
+
+/* Down or "P" operation on a semaphore.  Waits for SEMA's value
+   to become positive and then atomically decrements it.
+
+   This function may sleep, so it must not be called within an
+   interrupt handler.  This function may be called with
+   interrupts disabled, but if it sleeps then the next scheduled
+   thread will probably turn interrupts back on. */
+void
+sema_down (struct semaphore *sema) 
+{
+  enum intr_level old_level;
+
+  ASSERT (sema != NULL);
+  ASSERT (!intr_context ());
+
+  old_level = intr_disable ();
+  while (sema->value == 0) 
+    {
+      list_push_back (&sema->waiters, &thread_current ()->elem);
+      thread_block ();
+    }
+  sema->value--;
+  intr_set_level (old_level);
+}
+
+/* Down or "P" operation on a semaphore, but only if the
+   semaphore is not already 0.  Returns true if the semaphore is
+   decremented, false otherwise.
+
+   This function may be called from an interrupt handler. */
+bool
+sema_try_down (struct semaphore *sema) 
+{
+  enum intr_level old_level;
+  bool success;
+
+  ASSERT (sema != NULL);
+
+  old_level = intr_disable ();
+  if (sema->value > 0) 
+    {
+      sema->value--;
+      success = true; 
+    }
+  else
+    success = false;
+  intr_set_level (old_level);
+
+  return success;
+}
+
+/* Up or "V" operation on a semaphore.  Increments SEMA's value
+   and wakes up one thread of those waiting for SEMA, if any.
+
+   This function may be called from an interrupt handler. */
+void
+sema_up (struct semaphore *sema) 
+{
+  enum intr_level old_level;
+
+  ASSERT (sema != NULL);
+
+  old_level = intr_disable ();
+  // if (!list_empty (&sema->waiters)) 
+  //   thread_unblock (list_entry (list_pop_front (&sema->waiters),
+  //                               struct thread, elem));
+  /* ----- Newly Add for Proj01 Threads ----- */
+  if (!list_empty (&sema->waiters)) 
+  {
+    /* Maintain a priority queue for sema_up, 
+      to take out the highest priority thread in waiters. */
+    list_sort (&sema->waiters, cmp_thread_priority, NULL);
+    thread_unblock (list_entry (list_pop_front (&sema->waiters),
+                                struct thread, elem));
+  }
+  /* ----- Newly Add for Proj01 Threads ----- */
+  sema->value++;
+  // thread_yield ();
+  intr_set_level (old_level);
+}
+
+static void sema_test_helper (void *sema_);
+
+/* Self-test for semaphores that makes control "ping-pong"
+   between a pair of threads.  Insert calls to printf() to see
+   what's going on. */
+void
+sema_self_test (void) 
+{
+  struct semaphore sema[2];
+  int i;
+
+  printf ("Testing semaphores...");
+  sema_init (&sema[0], 0);
+  sema_init (&sema[1], 0);
+  thread_create ("sema-test", PRI_DEFAULT, sema_test_helper, &sema);
+  for (i = 0; i < 10; i++) 
+    {
+      sema_up (&sema[0]);
+      sema_down (&sema[1]);
+    }
+  printf ("done.\n");
+}
+
+/* Thread function used by sema_self_test(). */
+static void
+sema_test_helper (void *sema_) 
+{
+  struct semaphore *sema = sema_;
+  int i;
+
+  for (i = 0; i < 10; i++) 
+    {
+      sema_down (&sema[0]);
+      sema_up (&sema[1]);
+    }
+}
+
+/* Initializes LOCK.  A lock can be held by at most a single
+   thread at any given time.  Our locks are not "recursive", that
+   is, it is an error for the thread currently holding a lock to
+   try to acquire that lock.
+
+   A lock is a specialization of a semaphore with an initial
+   value of 1.  The difference between a lock and such a
+   semaphore is twofold.  First, a semaphore can have a value
+   greater than 1, but a lock can only be owned by a single
+   thread at a time.  Second, a semaphore does not have an owner,
+   meaning that one thread can "down" the semaphore and then
+   another one "up" it, but with a lock the same thread must both
+   acquire and release it.  When these restrictions prove
+   onerous, it's a good sign that a semaphore should be used,
+   instead of a lock. */
+void
+lock_init (struct lock *lock)
+{
+  ASSERT (lock != NULL);
+
+  lock->holder = NULL;
+  sema_init (&lock->semaphore, 1);
+  /* ----- Newly Add for Proj01 Threads ----- */
+  lock->max_priority = 0;
+  /* ----- Newly Add for Proj01 Threads ----- */
+}
+
+/* Acquires LOCK, sleeping until it becomes available if
+   necessary.  The lock must not already be held by the current
+   thread.
+
+   This function may sleep, so it must not be called within an
+   interrupt handler.  This function may be called with
+   interrupts disabled, but interrupts will be turned back on if
+   we need to sleep. */
+void
+lock_acquire (struct lock *lock)
+{
+  ASSERT (lock != NULL);
+  ASSERT (!intr_context ());
+  // ASSERT (!lock_held_by_current_thread (lock));
+  if (lock_held_by_current_thread (lock))
+  {
+    return;
+  }
+
+  // struct thread *cur = thread_current();
+  // struct thread *holder = lock->holder;
+
+  /* ----- Newly Add for Proj01 Threads ----- */
+  // /* Update each holder's priority recursively to ensure that
+  //    all of the lock_holders have the highest priority from the 
+  //    locks they hold. */
+  // if (holder != NULL && !thread_mlfqs)
+  // {
+  //   cur->lock_waiting = lock;
+  //   struct lock *tmp_lock = lock;
+  //   while (tmp_lock != NULL)
+  //   {
+  //     if (cur->priority < tmp_lock->max_priority) {break;}
+  //     tmp_lock->max_priority = cur->priority;
+  //     struct list_elem *max_lock = list_max(&tmp_lock->holder->locks_hold, lock_max_priority, NULL);
+  //     int max_lock_priority = list_entry(max_lock, struct lock, elem)->max_priority;
+  //     if (tmp_lock->holder->priority < max_lock_priority)
+  //     {
+  //       tmp_lock->holder->priority = max_lock_priority;
+  //     }
+  //     tmp_lock = tmp_lock->holder->lock_waiting;
+  //   }
+  // }
+  /* ----- Newly Add for Proj01 Threads ----- */
+
+  sema_down (&lock->semaphore);
+  lock->holder = thread_current ();
+
+  /* ----- Newly Add for Proj01 Threads ----- */
+  // /* Now the thread get the lock, so remove lock_waiting and update
+  //    the lock's priority and update cur's locks_hold. Since the thread 
+  //    get the lock must be the thread waiting for the lock who has the 
+  //    highest priority, we can directly set the max_priority to cur's priority. */
+  // if (!thread_mlfqs)
+  // {
+  //   thread_current () -> lock_waiting = NULL;
+  //   lock->max_priority = thread_current ()->priority;
+  //   list_push_back (&thread_current ()->locks_hold, &lock->elem);
+  //   if (lock->max_priority > thread_current ()->priority)
+  //   {
+  //     thread_current ()->priority = lock->max_priority;
+  //     // thread_yield ();
+  //   }
+  // }
+  /* ----- Newly Add for Proj01 Threads ----- */
+
+}
+
+/* Tries to acquires LOCK and returns true if successful or false
+   on failure.  The lock must not already be held by the current
+   thread.
+
+   This function will not sleep, so it may be called within an
+   interrupt handler. */
+bool
+lock_try_acquire (struct lock *lock)
+{
+  bool success;
+
+  ASSERT (lock != NULL);
+  ASSERT (!lock_held_by_current_thread (lock));
+
+  success = sema_try_down (&lock->semaphore);
+  if (success)
+    lock->holder = thread_current ();
+  return success;
+}
+
+/* Releases LOCK, which must be owned by the current thread.
+
+   An interrupt handler cannot acquire a lock, so it does not
+   make sense to try to release a lock within an interrupt
+   handler. */
+void
+lock_release (struct lock *lock) 
+{
+  ASSERT (lock != NULL);
+  // ASSERT (lock_held_by_current_thread (lock));
+  if (!lock_held_by_current_thread (lock))
+  {
+    return;
+  }
+
+  /* ----- Newly Add for Proj01 Threads ----- */
+  // /* Remove lock's elem from cur's locks_hold. Update cur's 
+  // priority to its remaining locks' highest max_priority. */
+
+  // if (!thread_mlfqs)
+  // {
+  //   struct thread *cur= thread_current ();
+  //   list_remove (&lock->elem);
+  //   int maximal = cur->old_priority;
+  //   if (!list_empty (&cur->locks_hold))
+  //   {
+  //     struct list_elem *max_lock = list_max (&cur->locks_hold, 
+  //                                            lock_max_priority, NULL);
+  //     int tmp = list_entry (max_lock, struct lock, elem)->max_priority;
+  //     if (tmp > maximal)
+  //     {
+  //       maximal = tmp;
+  //     }
+  //   }
+  //   cur->priority = maximal;
+  // }
+  /* ----- Newly Add for Proj01 Threads ----- */
+
+  lock->holder = NULL;
+  sema_up (&lock->semaphore);
+}
+
+/* Returns true if the current thread holds LOCK, false
+   otherwise.  (Note that testing whether some other thread holds
+   a lock would be racy.) */
+bool
+lock_held_by_current_thread (const struct lock *lock) 
+{
+  ASSERT (lock != NULL);
+
+  return lock->holder == thread_current ();
+}
+
+/* One semaphore in a list. */
+struct semaphore_elem 
+  {
+    struct list_elem elem;              /* List element. */
+    struct semaphore semaphore;         /* This semaphore. */
+  };
+
+/* Initializes condition variable COND.  A condition variable
+   allows one piece of code to signal a condition and cooperating
+   code to receive the signal and act upon it. */
+void
+cond_init (struct condition *cond)
+{
+  ASSERT (cond != NULL);
+
+  list_init (&cond->waiters);
+}
+
+/* Atomically releases LOCK and waits for COND to be signaled by
+   some other piece of code.  After COND is signaled, LOCK is
+   reacquired before returning.  LOCK must be held before calling
+   this function.
+
+   The monitor implemented by this function is "Mesa" style, not
+   "Hoare" style, that is, sending and receiving a signal are not
+   an atomic operation.  Thus, typically the caller must recheck
+   the condition after the wait completes and, if necessary, wait
+   again.
+
+   A given condition variable is associated with only a single
+   lock, but one lock may be associated with any number of
+   condition variables.  That is, there is a one-to-many mapping
+   from locks to condition variables.
+
+   This function may sleep, so it must not be called within an
+   interrupt handler.  This function may be called with
+   interrupts disabled, but interrupts will be turned back on if
+   we need to sleep. */
+void
+cond_wait (struct condition *cond, struct lock *lock) 
+{
+  struct semaphore_elem waiter;
+
+  ASSERT (cond != NULL);
+  ASSERT (lock != NULL);
+  ASSERT (!intr_context ());
+  ASSERT (lock_held_by_current_thread (lock));
+  
+  sema_init (&waiter.semaphore, 0);
+  list_push_back (&cond->waiters, &waiter.elem);
+  lock_release (lock);
+  sema_down (&waiter.semaphore);
+  lock_acquire (lock);
+}
+
+/* If any threads are waiting on COND (protected by LOCK), then
+   this function signals one of them to wake up from its wait.
+   LOCK must be held before calling this function.
+
+   An interrupt handler cannot acquire a lock, so it does not
+   make sense to try to signal a condition variable within an
+   interrupt handler. */
+void
+cond_signal (struct condition *cond, struct lock *lock UNUSED) 
+{
+  ASSERT (cond != NULL);
+  ASSERT (lock != NULL);
+  ASSERT (!intr_context ());
+  ASSERT (lock_held_by_current_thread (lock));
+
+  if (!list_empty (&cond->waiters)) 
+    sema_up (&list_entry (list_pop_front (&cond->waiters),
+                          struct semaphore_elem, elem)->semaphore);
+}
+
+/* Wakes up all threads, if any, waiting on COND (protected by
+   LOCK).  LOCK must be held before calling this function.
+
+   An interrupt handler cannot acquire a lock, so it does not
+   make sense to try to signal a condition variable within an
+   interrupt handler. */
+void
+cond_broadcast (struct condition *cond, struct lock *lock) 
+{
+  ASSERT (cond != NULL);
+  ASSERT (lock != NULL);
+
+  while (!list_empty (&cond->waiters))
+    cond_signal (cond, lock);
+}
+
+
+/* ----- Newly Add for Proj01 Threads ----- */
+/* cond sema comparation function */
+bool
+cmp_cond_priority (const struct list_elem *a, const struct list_elem *b, void *aux)
+{
+  struct semaphore_elem *sa = list_entry (a, struct semaphore_elem, elem);
+  struct semaphore_elem *sb = list_entry (b, struct semaphore_elem, elem);
+  return list_entry(list_front(&sa->semaphore.waiters), struct thread, elem)->priority
+         > list_entry(list_front(&sb->semaphore.waiters), struct thread, elem)->priority;
+}
+
+bool
+lock_max_priority (const struct list_elem *a,const struct list_elem *b,void *aux)
+{
+  return list_entry (a,struct lock,elem)->max_priority < list_entry (b,struct lock,elem)->max_priority;
+}
+/* ----- Newly Add for Proj01 Threads ----- */
\ No newline at end of file
diff --git a/src/threads/synch.h b/src/threads/synch.h
new file mode 100644
index 0000000..1fcebbf
--- /dev/null
+++ b/src/threads/synch.h
@@ -0,0 +1,59 @@
+#ifndef THREADS_SYNCH_H
+#define THREADS_SYNCH_H
+
+#include <list.h>
+#include <stdbool.h>
+
+/* A counting semaphore. */
+struct semaphore 
+  {
+    unsigned value;             /* Current value. */
+    struct list waiters;        /* List of waiting threads. */
+  };
+
+void sema_init (struct semaphore *, unsigned value);
+void sema_down (struct semaphore *);
+bool sema_try_down (struct semaphore *);
+void sema_up (struct semaphore *);
+void sema_self_test (void);
+
+/* Lock. */
+struct lock 
+  {
+    struct thread *holder;      /* Thread holding lock (for debugging). */
+    struct semaphore semaphore; /* Binary semaphore controlling access. */
+    
+    /* ----- Newly Add for Proj01 Threads ----- */
+    int max_priority;    	      /* Record the highest priority of threads that need this lock. */
+    struct list_elem elem;      /* Similar to list_elem in thread. */
+    /* ----- Newly Add for Proj01 Threads ----- */
+  };
+
+void lock_init (struct lock *);
+void lock_acquire (struct lock *);
+bool lock_try_acquire (struct lock *);
+void lock_release (struct lock *);
+bool lock_held_by_current_thread (const struct lock *);
+/* ----- Newly Add for Proj01 Threads ----- */
+bool lock_max_priority (const struct list_elem *a,const struct list_elem *b,void *aux);
+/* ----- Newly Add for Proj01 Threads ----- */
+
+/* Condition variable. */
+struct condition 
+  {
+    struct list waiters;        /* List of waiting threads. */
+  };
+
+void cond_init (struct condition *);
+void cond_wait (struct condition *, struct lock *);
+void cond_signal (struct condition *, struct lock *);
+void cond_broadcast (struct condition *, struct lock *);
+
+/* Optimization barrier.
+
+   The compiler will not reorder operations across an
+   optimization barrier.  See "Optimization Barriers" in the
+   reference guide for more information.*/
+#define barrier() asm volatile ("" : : : "memory")
+
+#endif /* threads/synch.h */
diff --git a/src/threads/thread.c b/src/threads/thread.c
new file mode 100644
index 0000000..c3760d8
--- /dev/null
+++ b/src/threads/thread.c
@@ -0,0 +1,749 @@
+#include "threads/thread.h"
+#include "list.h"
+#include <debug.h>
+#include <stddef.h>
+#include <random.h>
+#include <stdio.h>
+#include <string.h>
+#include <filesys/filesys.h>
+#include "filesys/file.h"
+#include "threads/flags.h"
+#include "threads/interrupt.h"
+#include "threads/intr-stubs.h"
+#include "threads/palloc.h"
+#include "threads/switch.h"
+#include "threads/synch.h"
+#include "threads/vaddr.h"
+
+#ifdef USERPROG
+#include "userprog/process.h"
+#include "malloc.h"
+#endif
+
+#ifdef VM
+#include <vm/page.h>
+#include <userprog/syscall.h>
+#endif
+
+
+/* Random value for struct thread's `magic' member.
+   Used to detect stack overflow.  See the big comment at the top
+   of thread.h for details. */
+#define THREAD_MAGIC 0xcd6abf4b
+
+/* List of processes in THREAD_READY state, that is, processes
+   that are ready to run but not actually running. */
+static struct list ready_list;
+
+/* List of all processes.  Processes are added to this list
+   when they are first scheduled and removed when they exit. */
+static struct list all_list;
+
+/* Idle thread. */
+static struct thread *idle_thread;
+
+/* Initial thread, the thread running init.c:main(). */
+static struct thread *initial_thread;
+
+/* Lock used by allocate_tid(). */
+static struct lock tid_lock;
+
+/* Stack frame for kernel_thread(). */
+struct kernel_thread_frame 
+  {
+    void *eip;                  /* Return address. */
+    thread_func *function;      /* Function to call. */
+    void *aux;                  /* Auxiliary data for function. */
+  };
+
+/* Statistics. */
+static long long idle_ticks;    /* # of timer ticks spent idle. */
+static long long kernel_ticks;  /* # of timer ticks in kernel threads. */
+static long long user_ticks;    /* # of timer ticks in user programs. */
+
+/* Scheduling. */
+#define TIME_SLICE 4            /* # of timer ticks to give each thread. */
+static unsigned thread_ticks;   /* # of timer ticks since last yield. */
+
+/* If false (default), use round-robin scheduler.
+   If true, use multi-level feedback queue scheduler.
+   Controlled by kernel command-line option "-o mlfqs". */
+bool thread_mlfqs;
+
+static void kernel_thread (thread_func *, void *aux);
+
+static void idle (void *aux UNUSED);
+static struct thread *running_thread (void);
+static struct thread *next_thread_to_run (void);
+static void init_thread (struct thread *, const char *name, int priority);
+static bool is_thread (struct thread *) UNUSED;
+static void *alloc_frame (struct thread *, size_t size);
+static void schedule (void);
+void thread_schedule_tail (struct thread *prev);
+static tid_t allocate_tid (void);
+
+/* ----- Newly Add for Proj02 UserProg ----- */
+/* Use a lock to lock process when do file operation. */
+static struct lock lock_file;
+
+void 
+acquire_lock_file (void)
+{
+  lock_acquire(&lock_file);
+}
+
+void 
+release_lock_file (void)
+{
+  lock_release(&lock_file);
+}
+/* ----- Newly Add for Proj02 UserProg ----- */
+
+/* Initializes the threading system by transforming the code
+   that's currently running into a thread.  This can't work in
+   general and it is possible in this case only because loader.S
+   was careful to put the bottom of the stack at a page boundary.
+
+   Also initializes the run queue and the tid lock.
+
+   After calling this function, be sure to initialize the page
+   allocator before trying to create any threads with
+   thread_create().
+
+   It is not safe to call thread_current() until this function
+   finishes. */
+void
+thread_init (void) 
+{
+  ASSERT (intr_get_level () == INTR_OFF);
+
+  lock_init (&tid_lock);
+  list_init (&ready_list);
+  list_init (&all_list);
+
+  /* ----- Newly Add for Proj02 UserProg ----- */
+  lock_init(&lock_file);
+  /* ----- Newly Add for Proj02 UserProg ----- */
+
+  /* Set up a thread structure for the running thread. */
+  initial_thread = running_thread ();
+  init_thread (initial_thread, "main", PRI_DEFAULT);
+  initial_thread->status = THREAD_RUNNING;
+  initial_thread->tid = allocate_tid ();
+}
+
+/* Starts preemptive thread scheduling by enabling interrupts.
+   Also creates the idle thread. */
+void
+thread_start (void) 
+{
+  /* Create the idle thread. */
+  struct semaphore idle_started;
+  sema_init (&idle_started, 0);
+  thread_create ("idle", PRI_MIN, idle, &idle_started);
+
+  /* Start preemptive thread scheduling. */
+  intr_enable ();
+
+  /* Wait for the idle thread to initialize idle_thread. */
+  sema_down (&idle_started);
+}
+
+/* Called by the timer interrupt handler at each timer tick.
+   Thus, this function runs in an external interrupt context. */
+void
+thread_tick (void) 
+{
+  struct thread *t = thread_current ();
+
+  /* Update statistics. */
+  if (t == idle_thread)
+    idle_ticks++;
+#ifdef USERPROG
+  else if (t->pagedir != NULL)
+    user_ticks++;
+#endif
+  else
+    kernel_ticks++;
+
+  /* Enforce preemption. */
+  if (++thread_ticks >= TIME_SLICE)
+    intr_yield_on_return ();
+}
+
+/* Prints thread statistics. */
+void
+thread_print_stats (void) 
+{
+  printf ("Thread: %lld idle ticks, %lld kernel ticks, %lld user ticks\n",
+          idle_ticks, kernel_ticks, user_ticks);
+}
+
+/* Creates a new kernel thread named NAME with the given initial
+   PRIORITY, which executes FUNCTION passing AUX as the argument,
+   and adds it to the ready queue.  Returns the thread identifier
+   for the new thread, or TID_ERROR if creation fails.
+
+   If thread_start() has been called, then the new thread may be
+   scheduled before thread_create() returns.  It could even exit
+   before thread_create() returns.  Contrariwise, the original
+   thread may run for any amount of time before the new thread is
+   scheduled.  Use a semaphore or some other form of
+   synchronization if you need to ensure ordering.
+
+   The code provided sets the new thread's `priority' member to
+   PRIORITY, but no actual priority scheduling is implemented.
+   Priority scheduling is the goal of Problem 1-3. */
+tid_t
+thread_create (const char *name, int priority,
+               thread_func *function, void *aux) 
+{
+  struct thread *t;
+  struct kernel_thread_frame *kf;
+  struct switch_entry_frame *ef;
+  struct switch_threads_frame *sf;
+  tid_t tid;
+
+  ASSERT (function != NULL);
+
+  /* Allocate thread. */
+  t = palloc_get_page (PAL_ZERO);
+  if (t == NULL)
+    return TID_ERROR;
+
+  /* Initialize thread. */
+  init_thread (t, name, priority);
+  tid = t->tid = allocate_tid ();
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+  t->current_dir = thread_current()->current_dir;
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+  /* ----- Newly Add for Proj02 UserProg ----- */
+  /* Initialize the child thead when create a new thread. */
+  t->child_thread = malloc(sizeof(struct child));
+
+  /* The tid of the child thread is the tid of 
+     the new thread itself initially. */
+  t->child_thread->tid = tid;
+  sema_init (&t->child_thread->sema_waiting, 0);
+  /* Equivalent to put the new thread into the list of child threads. */
+  list_push_back (&thread_current ()->children_list, 
+                  &t->child_thread->child_elem);
+
+  /* Initialize the exit status. */
+  t->child_thread->stored_exit_status = UINT32_MAX;
+  t->child_thread->has_been_waited = false;
+  /* ----- Newly Add for Proj02 UserProg ----- */
+
+  /* Stack frame for kernel_thread(). */
+  kf = alloc_frame (t, sizeof *kf);
+  kf->eip = NULL;
+  kf->function = function;
+  kf->aux = aux;
+
+  /* Stack frame for switch_entry(). */
+  ef = alloc_frame (t, sizeof *ef);
+  ef->eip = (void (*) (void)) kernel_thread;
+
+  /* Stack frame for switch_threads(). */
+  sf = alloc_frame (t, sizeof *sf);
+  sf->eip = switch_entry;
+  sf->ebp = 0;
+
+  /* Add to run queue. */
+  thread_unblock (t);
+
+  return tid;
+}
+
+/* Puts the current thread to sleep.  It will not be scheduled
+   again until awoken by thread_unblock().
+
+   This function must be called with interrupts turned off.  It
+   is usually a better idea to use one of the synchronization
+   primitives in synch.h. */
+void
+thread_block (void) 
+{
+  ASSERT (!intr_context ());
+  ASSERT (intr_get_level () == INTR_OFF);
+
+  thread_current ()->status = THREAD_BLOCKED;
+  schedule ();
+}
+
+/* Transitions a blocked thread T to the ready-to-run state.
+   This is an error if T is not blocked.  (Use thread_yield() to
+   make the running thread ready.)
+
+   This function does not preempt the running thread.  This can
+   be important: if the caller had disabled interrupts itself,
+   it may expect that it can atomically unblock a thread and
+   update other data. */
+void
+thread_unblock (struct thread *t) 
+{
+  enum intr_level old_level;
+
+  ASSERT (is_thread (t));
+
+  old_level = intr_disable ();
+  ASSERT (t->status == THREAD_BLOCKED);
+  list_push_back (&ready_list, &t->elem);
+  t->status = THREAD_READY;
+  intr_set_level (old_level);
+}
+
+/* Returns the name of the running thread. */
+const char *
+thread_name (void) 
+{
+  return thread_current ()->name;
+}
+
+/* Returns the running thread.
+   This is running_thread() plus a couple of sanity checks.
+   See the big comment at the top of thread.h for details. */
+struct thread *
+thread_current (void) 
+{
+  struct thread *t = running_thread ();
+  
+  /* Make sure T is really a thread.
+     If either of these assertions fire, then your thread may
+     have overflowed its stack.  Each thread has less than 4 kB
+     of stack, so a few big automatic arrays or moderate
+     recursion can cause stack overflow. */
+  ASSERT (is_thread (t));
+  ASSERT (t->status == THREAD_RUNNING);
+
+  return t;
+}
+
+/* Returns the running thread's tid. */
+tid_t
+thread_tid (void) 
+{
+  return thread_current ()->tid;
+}
+
+/* Deschedules the current thread and destroys it.  Never
+   returns to the caller. */
+void
+thread_exit (void) 
+{
+  ASSERT (!intr_context ());
+
+#ifdef USERPROG
+  process_exit ();
+#endif
+
+  /* Remove thread from all threads list, set our status to dying,
+     and schedule another process.  That process will destroy us
+     when it calls thread_schedule_tail(). */
+  intr_disable ();
+
+  /* ----- Newly Add for Proj02 UserProg ----- */
+  /* Print exit information. */
+  printf ("%s: exit(%d)\n", thread_name(), thread_current()->exit_status);
+
+  /* Keep track of exit_status for the return value in process_wait(). */
+  thread_current ()->child_thread->stored_exit_status = thread_current()->exit_status;
+  /* Sema up the semaphore to wake up process_wait(). */
+  sema_up (&thread_current()->child_thread->sema_waiting);
+  acquire_lock_file ();
+  file_close (thread_current()->file_owned);
+  release_lock_file();
+
+  /* Close all the files when a thread exit. */
+  struct list_elem *f_elem;
+  struct list *files = &thread_current ()->files_list;
+  while (!list_empty (files))
+  {
+    f_elem = list_pop_front (files);
+    struct thread_file *f_ptr = list_entry (f_elem, struct thread_file, file_elem);
+
+    /* Close the file, remove it from files_list, free its resources. */
+    acquire_lock_file ();
+    file_close (f_ptr->file);
+    release_lock_file ();
+    list_remove (f_elem);
+    free (f_ptr);
+  }
+  /* ----- Newly Add for Proj02 UserProg ----- */
+
+  list_remove (&thread_current()->allelem);
+  thread_current ()->status = THREAD_DYING;
+  schedule ();
+  NOT_REACHED ();
+}
+
+/* Yields the CPU.  The current thread is not put to sleep and
+   may be scheduled again immediately at the scheduler's whim. */
+void
+thread_yield (void) 
+{
+  struct thread *cur = thread_current ();
+  enum intr_level old_level;
+  
+  ASSERT (!intr_context ());
+
+  old_level = intr_disable ();
+  if (cur != idle_thread) 
+    list_push_back (&ready_list, &cur->elem);
+  cur->status = THREAD_READY;
+  schedule ();
+  intr_set_level (old_level);
+}
+
+/* Invoke function 'func' on all threads, passing along 'aux'.
+   This function must be called with interrupts off. */
+void
+thread_foreach (thread_action_func *func, void *aux)
+{
+  struct list_elem *e;
+
+  ASSERT (intr_get_level () == INTR_OFF);
+
+  for (e = list_begin (&all_list); e != list_end (&all_list);
+       e = list_next (e))
+    {
+      struct thread *t = list_entry (e, struct thread, allelem);
+      func (t, aux);
+    }
+}
+
+/* Sets the current thread's priority to NEW_PRIORITY. */
+void
+thread_set_priority (int new_priority) 
+{
+  thread_current ()->priority = new_priority;
+}
+
+/* Returns the current thread's priority. */
+int
+thread_get_priority (void) 
+{
+  return thread_current ()->priority;
+}
+
+/* Sets the current thread's nice value to NICE. */
+void
+thread_set_nice (int nice UNUSED) 
+{
+  /* Not yet implemented. */
+}
+
+/* Returns the current thread's nice value. */
+int
+thread_get_nice (void) 
+{
+  /* Not yet implemented. */
+  return 0;
+}
+
+/* Returns 100 times the system load average. */
+int
+thread_get_load_avg (void) 
+{
+  /* Not yet implemented. */
+  return 0;
+}
+
+/* Returns 100 times the current thread's recent_cpu value. */
+int
+thread_get_recent_cpu (void) 
+{
+  /* Not yet implemented. */
+  return 0;
+}
+
+/* Idle thread.  Executes when no other thread is ready to run.
+
+   The idle thread is initially put on the ready list by
+   thread_start().  It will be scheduled once initially, at which
+   point it initializes idle_thread, "up"s the semaphore passed
+   to it to enable thread_start() to continue, and immediately
+   blocks.  After that, the idle thread never appears in the
+   ready list.  It is returned by next_thread_to_run() as a
+   special case when the ready list is empty. */
+static void
+idle (void *idle_started_ UNUSED) 
+{
+  struct semaphore *idle_started = idle_started_;
+  idle_thread = thread_current ();
+  sema_up (idle_started);
+
+  for (;;) 
+    {
+      /* Let someone else run. */
+      intr_disable ();
+      thread_block ();
+
+      /* Re-enable interrupts and wait for the next one.
+
+         The `sti' instruction disables interrupts until the
+         completion of the next instruction, so these two
+         instructions are executed atomically.  This atomicity is
+         important; otherwise, an interrupt could be handled
+         between re-enabling interrupts and waiting for the next
+         one to occur, wasting as much as one clock tick worth of
+         time.
+
+         See [IA32-v2a] "HLT", [IA32-v2b] "STI", and [IA32-v3a]
+         7.11.1 "HLT Instruction". */
+      asm volatile ("sti; hlt" : : : "memory");
+    }
+}
+
+/* Function used as the basis for a kernel thread. */
+static void
+kernel_thread (thread_func *function, void *aux) 
+{
+  ASSERT (function != NULL);
+
+  intr_enable ();       /* The scheduler runs with interrupts off. */
+  function (aux);       /* Execute the thread function. */
+  thread_exit ();       /* If function() returns, kill the thread. */
+}
+
+/* Returns the running thread. */
+struct thread *
+running_thread (void) 
+{
+  uint32_t *esp;
+
+  /* Copy the CPU's stack pointer into `esp', and then round that
+     down to the start of a page.  Because `struct thread' is
+     always at the beginning of a page and the stack pointer is
+     somewhere in the middle, this locates the curent thread. */
+  asm ("mov %%esp, %0" : "=g" (esp));
+  return pg_round_down (esp);
+}
+
+/* Returns true if T appears to point to a valid thread. */
+static bool
+is_thread (struct thread *t)
+{
+  return t != NULL && t->magic == THREAD_MAGIC;
+}
+
+/* Does basic initialization of T as a blocked thread named
+   NAME. */
+static void
+init_thread (struct thread *t, const char *name, int priority)
+{
+  enum intr_level old_level;
+
+  ASSERT (t != NULL);
+  ASSERT (PRI_MIN <= priority && priority <= PRI_MAX);
+  ASSERT (name != NULL);
+
+  memset (t, 0, sizeof *t);
+  t->status = THREAD_BLOCKED;
+  strlcpy (t->name, name, sizeof t->name);
+  t->stack = (uint8_t *) t + PGSIZE;
+  t->priority = priority;
+  t->magic = THREAD_MAGIC;
+
+  /* ----- Newly Add for Proj02 UserProg ----- */
+  /* For Syscall */
+  /* Record the parent's thread */
+  if (t == initial_thread) 
+  {
+    t->parent_thread = NULL;
+  }
+  else 
+  {
+    t->parent_thread = thread_current ();
+  }
+  /* List initialization for lists */
+  list_init (&t->children_list);
+  list_init (&t->files_list);
+  /* Semaphore initialization for lists */
+  sema_init (&t->sema_waiting, 0);
+  t->child_success = true;
+  /* Initialize exit status to MAX */
+  t->exit_status = UINT32_MAX;
+  t->max_file_fd = 2;
+  t->file_owned = NULL;
+  /* ----- Newly Add for Proj02 UserProg ----- */
+
+#ifdef VM
+  list_init(&t->mmap_file_list);
+  t->next_mapid = 1;
+#endif
+  
+  old_level = intr_disable ();
+  list_push_back (&all_list, &t->allelem);
+  intr_set_level (old_level);
+}
+
+/* Allocates a SIZE-byte frame at the top of thread T's stack and
+   returns a pointer to the frame's base. */
+static void *
+alloc_frame (struct thread *t, size_t size) 
+{
+  /* Stack data is always allocated in word-size units. */
+  ASSERT (is_thread (t));
+  ASSERT (size % sizeof (uint32_t) == 0);
+
+  t->stack -= size;
+  return t->stack;
+}
+
+/* Chooses and returns the next thread to be scheduled.  Should
+   return a thread from the run queue, unless the run queue is
+   empty.  (If the running thread can continue running, then it
+   will be in the run queue.)  If the run queue is empty, return
+   idle_thread. */
+static struct thread *
+next_thread_to_run (void) 
+{
+  if (list_empty (&ready_list))
+    return idle_thread;
+  else
+    return list_entry (list_pop_front (&ready_list), struct thread, elem);
+}
+
+/* Completes a thread switch by activating the new thread's page
+   tables, and, if the previous thread is dying, destroying it.
+
+   At this function's invocation, we just switched from thread
+   PREV, the new thread is already running, and interrupts are
+   still disabled.  This function is normally invoked by
+   thread_schedule() as its final action before returning, but
+   the first time a thread is scheduled it is called by
+   switch_entry() (see switch.S).
+
+   It's not safe to call printf() until the thread switch is
+   complete.  In practice that means that printf()s should be
+   added at the end of the function.
+
+   After this function and its caller returns, the thread switch
+   is complete. */
+void
+thread_schedule_tail (struct thread *prev)
+{
+  struct thread *cur = running_thread ();
+  
+  ASSERT (intr_get_level () == INTR_OFF);
+
+  /* Mark us as running. */
+  cur->status = THREAD_RUNNING;
+
+  /* Start new time slice. */
+  thread_ticks = 0;
+
+#ifdef USERPROG
+  /* Activate the new address space. */
+  process_activate ();
+#endif
+
+  /* If the thread we switched from is dying, destroy its struct
+     thread.  This must happen late so that thread_exit() doesn't
+     pull out the rug under itself.  (We don't free
+     initial_thread because its memory was not obtained via
+     palloc().) */
+  if (prev != NULL && prev->status == THREAD_DYING && prev != initial_thread) 
+    {
+      ASSERT (prev != cur);
+      palloc_free_page (prev);
+    }
+}
+
+/* Schedules a new process.  At entry, interrupts must be off and
+   the running process's state must have been changed from
+   running to some other state.  This function finds another
+   thread to run and switches to it.
+
+   It's not safe to call printf() until thread_schedule_tail()
+   has completed. */
+static void
+schedule (void) 
+{
+  struct thread *cur = running_thread ();
+  struct thread *next = next_thread_to_run ();
+  struct thread *prev = NULL;
+
+  ASSERT (intr_get_level () == INTR_OFF);
+  ASSERT (cur->status != THREAD_RUNNING);
+  ASSERT (is_thread (next));
+
+  if (cur != next)
+    prev = switch_threads (cur, next);
+  thread_schedule_tail (prev);
+}
+
+/* Returns a tid to use for a new thread. */
+static tid_t
+allocate_tid (void) 
+{
+  static tid_t next_tid = 1;
+  tid_t tid;
+
+  lock_acquire (&tid_lock);
+  tid = next_tid++;
+  lock_release (&tid_lock);
+
+  return tid;
+}
+
+/* Terminate thread with a return value FINAL_VALUE */
+void
+thread_exit_with_return_value(struct intr_frame *f, int return_value) {
+  struct thread *cur = thread_current();
+  cur->return_value = return_value;
+  f->eax = (uint32_t)return_value;
+  thread_exit();
+}
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+#ifdef FILESYS
+/* Get the file_handle pointer according to fd
+ * Return NULL if fd is invalid
+ * */
+struct thread_file* syscall_get_thread_file(int fd){
+  struct thread* cur =  thread_current();
+  struct list_elem* i;
+  for (i = list_begin(&cur->files_list); i != list_end(&cur->files_list); i = list_next(i)){
+    struct thread_file* t;
+    t = list_entry(i, struct thread_file, file_elem);
+    if (t->fd == fd){
+      if (t->owned_thread != cur)
+        return NULL;
+      else
+        return t;
+    }
+  }
+  return NULL;
+}
+
+/*
+ * Set main thread's current directory
+ * MUST use after filesys init
+ * */
+void set_main_thread_dir()
+{
+  initial_thread->current_dir = dir_open_root();
+}
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+/* Offset of `stack' member within `struct thread'.
+   Used by switch.S, which can't figure it out on its own. */
+uint32_t thread_stack_ofs = offsetof (struct thread, stack);
+
+/* ----- Newly Add for Proj01 Threads ----- */
+bool cmp_thread_priority (const struct list_elem *a,
+                            const struct list_elem *b,
+                            void *aux)
+{
+  return list_entry(a, struct thread, elem)->priority
+   > list_entry(b, struct thread, elem)->priority;
+}
+/* ----- Newly Add for Proj01 Threads ----- */
diff --git a/src/threads/thread.h b/src/threads/thread.h
new file mode 100644
index 0000000..811c4f0
--- /dev/null
+++ b/src/threads/thread.h
@@ -0,0 +1,251 @@
+#ifndef THREADS_THREAD_H
+#define THREADS_THREAD_H
+
+#include <debug.h>
+#include <list.h>
+#include <stdint.h>
+#include <threads/synch.h>
+
+#include "../lib/kernel/hash.h"
+#include "threads/interrupt.h"
+#include "filesys/off_t.h"
+#include "filesys/directory.h"
+
+typedef int mapid_t;
+
+/* States in a thread's life cycle. */
+enum thread_status
+  {
+    THREAD_RUNNING,     /* Running thread. */
+    THREAD_READY,       /* Not running but ready to run. */
+    THREAD_BLOCKED,     /* Waiting for an event to trigger. */
+    THREAD_DYING        /* About to be destroyed. */
+  };
+
+/* Thread identifier type.
+   You can redefine this to whatever type you like. */
+typedef int tid_t;
+#define TID_ERROR ((tid_t) -1)          /* Error value for tid_t. */
+
+/* Thread priorities. */
+#define PRI_MIN 0                       /* Lowest priority. */
+#define PRI_DEFAULT 31                  /* Default priority. */
+#define PRI_MAX 63                      /* Highest priority. */
+
+/* A kernel thread or user process.
+
+   Each thread structure is stored in its own 4 kB page.  The
+   thread structure itself sits at the very bottom of the page
+   (at offset 0).  The rest of the page is reserved for the
+   thread's kernel stack, which grows downward from the top of
+   the page (at offset 4 kB).  Here's an illustration:
+
+        4 kB +---------------------------------+
+             |          kernel stack           |
+             |                |                |
+             |                |                |
+             |                V                |
+             |         grows downward          |
+             |                                 |
+             |                                 |
+             |                                 |
+             |                                 |
+             |                                 |
+             |                                 |
+             |                                 |
+             |                                 |
+             +---------------------------------+
+             |              magic              |
+             |                :                |
+             |                :                |
+             |               name              |
+             |              status             |
+        0 kB +---------------------------------+
+
+   The upshot of this is twofold:
+
+      1. First, `struct thread' must not be allowed to grow too
+         big.  If it does, then there will not be enough room for
+         the kernel stack.  Our base `struct thread' is only a
+         few bytes in size.  It probably should stay well under 1
+         kB.
+
+      2. Second, kernel stacks must not be allowed to grow too
+         large.  If a stack overflows, it will corrupt the thread
+         state.  Thus, kernel functions should not allocate large
+         structures or arrays as non-static local variables.  Use
+         dynamic allocation with malloc() or palloc_get_page()
+         instead.
+
+   The first symptom of either of these problems will probably be
+   an assertion failure in thread_current(), which checks that
+   the `magic' member of the running thread's `struct thread' is
+   set to THREAD_MAGIC.  Stack overflow will normally change this
+   value, triggering the assertion. */
+/* The `elem' member has a dual purpose.  It can be an element in
+   the run queue (thread.c), or it can be an element in a
+   semaphore wait list (synch.c).  It can be used these two ways
+   only because they are mutually exclusive: only a thread in the
+   ready state is on the run queue, whereas only a thread in the
+   blocked state is on a semaphore wait list. */
+
+/* ----- Newly Add for Proj02 UserProg ----- */
+/* Defination for exec() and wait():
+   Child process of the parent process which does fork. */
+struct child
+  {
+    tid_t tid;                           /* Tid of the thread. */
+    bool has_been_waited;                /* Whether this child thread has been waited by its parent. */
+    struct list_elem child_elem;         /* List_elem of this child thread. */
+    struct semaphore sema_waiting;       /* Semaphore to control waiting. */
+    int stored_exit_status;              /* Exit status of this child thread. */
+  };
+  
+/* File that the thread open */
+struct thread_file
+  {
+    int fd;
+    struct file* file;
+    struct thread* owned_thread;
+    struct list_elem file_elem;
+    /* ----- Newly Add for Proj04 FileSys ----- */
+    #ifdef FILESYS
+        struct dir* opened_dir;
+    #endif
+    /* ----- Newly Add for Proj04 FileSys ----- */
+  };
+/* ----- Newly Add for Proj02 UserProg ----- */
+
+struct thread
+  {
+    /* Owned by thread.c. */
+    tid_t tid;                          /* Thread identifier. */
+    enum thread_status status;          /* Thread state. */
+    char name[16];                      /* Name (for debugging purposes). */
+    uint8_t *stack;                     /* Saved stack pointer. */
+    int priority;                       /* Priority. */
+    struct list_elem allelem;           /* List element for all threads list. */
+
+    /* Shared between thread.c and synch.c. */
+    struct list_elem elem;              /* List element. */
+
+    int return_value;
+
+    /* ----- Newly Add for Proj01 Threads ----- */
+    struct list locks_hold;   	        /* Locks in this threads' hand. */
+    struct lock *lock_waiting; 	        /* THe lock that block this thread. */
+    int old_priority;         	        /* The origin priority of this thread. */
+    /* ----- Newly Add for Proj01 Threads ----- */
+
+  #ifdef USERPROG
+    /* Owned by userprog/process.c. */
+    uint32_t *pagedir;                  /* Page directory. */
+
+    /* ----- Newly Add for Proj02 UserProg ----- */
+    /* Owned by userprog/process.c. */
+    /* Structure for Task2 */
+    int exit_status;                    /* Represent whether the file successfully exits or not. */
+    struct list children_list;          /* The list of all childs created. */
+    struct child *child_thread;         /* Store the child of this thread (itself when first create).*/
+    struct semaphore sema_waiting;      /* Control the child process's logic, finish parent waiting for child */
+    bool child_success;                 /* Judge whehter the child's thread execute successfully */
+    struct thread *parent_thread;       /* Parent thread of the thread.*/
+
+    /* Structure for Task3 */
+    struct list files_list;             /* List of opened files. */
+    int max_file_fd;                    /* Store max fd. */
+    struct file * file_owned;
+
+  /* ----- Newly Add for Proj02 UserProg ----- */
+  #endif
+
+  #ifdef VM
+  /* ----- Newly Add for Proj03 VM ----- */
+    struct hash* page_table;            /* Pointer to the page table used in VM system. */
+    void *esp;                          /* The current stack pointer for the thread. */
+    struct list mmap_file_list;         /* A list that stores the memory-mapped files associated with the thread. */
+    mapid_t next_mapid;                 /* The next map ID to be assigned to a new memory-mapped region. */
+  /* ----- Newly Add for Proj03 VM ----- */
+  #endif
+  
+/* ----- Newly Add for Proj04 FileSys ----- */
+  #ifdef FILESYS
+    struct dir *current_dir;
+  #endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+    /* Owned by thread.c. */
+    unsigned magic;                     /* Detects stack overflow. */
+  };
+
+
+/* ----- Newly Add for Proj03 VM ----- */
+struct mmap_file
+{
+    mapid_t mapid;               /* Unique identifier for the mmap. */
+    struct file* mmap_file;      /* File associated with the mmap. */
+    void* mmap_addr;             /* Target virtual address of the mmap. */
+    int num_page;                /* Number of pages occupied by the mmap file. */
+    int last_page_size;          /* Size of the last page (non-zero if file size is not aligned). */
+    struct list_elem elem;       /* List element for linking in a list. */
+    bool writable;               /* Flag indicating whether the mmap is writable. */
+    bool is_segment;             /* Flag indicating if this mmap is a segment. */
+    bool is_static_data;         /* Flag indicating whether it is static data. */
+    int num_page_with_segment;   /* Total number of pages including zero-filled pages. */
+    off_t file_ofs;              /* File offset where mmap starts. */
+};
+
+/* ----- Newly Add for Proj03 VM ----- */
+
+
+/* If false (default), use round-robin scheduler.
+   If true, use multi-level feedback queue scheduler.
+   Controlled by kernel command-line option "-o mlfqs". */
+extern bool thread_mlfqs;
+
+void thread_init (void);
+void thread_start (void);
+
+void thread_tick (void);
+void thread_print_stats (void);
+
+typedef void thread_func (void *aux);
+tid_t thread_create (const char *name, int priority, thread_func *, void *);
+
+void thread_block (void);
+void thread_unblock (struct thread *);
+
+struct thread *thread_current (void);
+tid_t thread_tid (void);
+const char *thread_name (void);
+
+void thread_exit (void) NO_RETURN;
+void thread_yield (void);
+
+/* Performs some operation on thread t, given auxiliary data AUX. */
+typedef void thread_action_func (struct thread *t, void *aux);
+void thread_foreach (thread_action_func *, void *);
+
+int thread_get_priority (void);
+void thread_set_priority (int);
+/* ----- Newly Add for Proj01 Threads ----- */
+list_less_func cmp_thread_priority;
+/* ----- Newly Add for Proj01 Threads ----- */
+
+int thread_get_nice (void);
+void thread_set_nice (int);
+int thread_get_recent_cpu (void);
+int thread_get_load_avg (void);
+
+void acquire_lock_file (void);
+void release_lock_file (void);
+
+void thread_exit_with_return_value(struct intr_frame *f, int return_value);
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+struct thread_file* syscall_get_thread_file(int fd);
+void set_main_thread_dir();
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+#endif /* threads/thread.h */
diff --git a/src/threads/vaddr.h b/src/threads/vaddr.h
new file mode 100644
index 0000000..184c824
--- /dev/null
+++ b/src/threads/vaddr.h
@@ -0,0 +1,89 @@
+#ifndef THREADS_VADDR_H
+#define THREADS_VADDR_H
+
+#include <debug.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+#include "threads/loader.h"
+
+/* Functions and macros for working with virtual addresses.
+
+   See pte.h for functions and macros specifically for x86
+   hardware page tables. */
+
+#define BITMASK(SHIFT, CNT) (((1ul << (CNT)) - 1) << (SHIFT))
+
+/* Page offset (bits 0:12). */
+#define PGSHIFT 0                          /* Index of first offset bit. */
+#define PGBITS  12                         /* Number of offset bits. */
+#define PGSIZE  (1 << PGBITS)              /* Bytes in a page. */
+#define PGMASK  BITMASK(PGSHIFT, PGBITS)   /* Page offset bits (0:12). */
+
+/* Offset within a page. */
+static inline unsigned pg_ofs (const void *va) {
+  return (uintptr_t) va & PGMASK;
+}
+
+/* Virtual page number. */
+static inline uintptr_t pg_no (const void *va) {
+  return (uintptr_t) va >> PGBITS;
+}
+
+/* Round up to nearest page boundary. */
+static inline void *pg_round_up (const void *va) {
+  return (void *) (((uintptr_t) va + PGSIZE - 1) & ~PGMASK);
+}
+
+/* Round down to nearest page boundary. */
+static inline void *pg_round_down (const void *va) {
+  return (void *) ((uintptr_t) va & ~PGMASK);
+}
+
+/* Base address of the 1:1 physical-to-virtual mapping.  Physical
+   memory is mapped starting at this virtual address.  Thus,
+   physical address 0 is accessible at PHYS_BASE, physical
+   address address 0x1234 at (uint8_t *) PHYS_BASE + 0x1234, and
+   so on.
+
+   This address also marks the end of user programs' address
+   space.  Up to this point in memory, user programs are allowed
+   to map whatever they like.  At this point and above, the
+   virtual address space belongs to the kernel. */
+#define	PHYS_BASE ((void *) LOADER_PHYS_BASE)
+
+/* Returns true if VADDR is a user virtual address. */
+static inline bool
+is_user_vaddr (const void *vaddr) 
+{
+  return vaddr < PHYS_BASE;
+}
+
+/* Returns true if VADDR is a kernel virtual address. */
+static inline bool
+is_kernel_vaddr (const void *vaddr) 
+{
+  return vaddr >= PHYS_BASE;
+}
+
+/* Returns kernel virtual address at which physical address PADDR
+   is mapped. */
+static inline void *
+ptov (uintptr_t paddr)
+{
+  ASSERT ((void *) paddr < PHYS_BASE);
+
+  return (void *) (paddr + PHYS_BASE);
+}
+
+/* Returns physical address at which kernel virtual address VADDR
+   is mapped. */
+static inline uintptr_t
+vtop (const void *vaddr)
+{
+  ASSERT (is_kernel_vaddr (vaddr));
+
+  return (uintptr_t) vaddr - (uintptr_t) PHYS_BASE;
+}
+
+#endif /* threads/vaddr.h */
diff --git a/src/userprog/.gitignore b/src/userprog/.gitignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/src/userprog/.gitignore
@@ -0,0 +1,3 @@
+build
+bochsrc.txt
+bochsout.txt
diff --git a/src/userprog/Make.vars b/src/userprog/Make.vars
new file mode 100644
index 0000000..e4dbb08
--- /dev/null
+++ b/src/userprog/Make.vars
@@ -0,0 +1,7 @@
+# -*- makefile -*-
+
+kernel.bin: DEFINES = -DUSERPROG -DFILESYS
+KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys
+TEST_SUBDIRS = tests/userprog tests/userprog/no-vm tests/filesys/base
+GRADING_FILE = $(SRCDIR)/tests/userprog/Grading
+SIMULATOR = --qemu
diff --git a/src/userprog/Makefile b/src/userprog/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/src/userprog/Makefile
@@ -0,0 +1 @@
+include ../Makefile.kernel
diff --git a/src/userprog/exception.c b/src/userprog/exception.c
new file mode 100644
index 0000000..f2fcc2f
--- /dev/null
+++ b/src/userprog/exception.c
@@ -0,0 +1,201 @@
+#include "userprog/exception.h"
+#include <inttypes.h>
+#include <stdio.h>
+#include "userprog/gdt.h"
+#include "threads/interrupt.h"
+#include "threads/thread.h"
+#include "userprog/pagedir.h"
+#include "../threads/vaddr.h"
+#include "syscall.h"
+
+#ifdef VM
+#include "vm/page.h"
+#include "syscall.h"
+#endif
+
+/* Number of page faults processed. */
+static long long page_fault_cnt;
+
+static void kill (struct intr_frame *);
+static void page_fault (struct intr_frame *);
+
+/* Registers handlers for interrupts that can be caused by user
+   programs.
+
+   In a real Unix-like OS, most of these interrupts would be
+   passed along to the user process in the form of signals, as
+   described in [SV-386] 3-24 and 3-25, but we don't implement
+   signals.  Instead, we'll make them simply kill the user
+   process.
+
+   Page faults are an exception.  Here they are treated the same
+   way as other exceptions, but this will need to change to
+   implement virtual memory.
+
+   Refer to [IA32-v3a] section 5.15 "Exception and Interrupt
+   Reference" for a description of each of these exceptions. */
+void
+exception_init (void) 
+{
+  /* These exceptions can be raised explicitly by a user program,
+     e.g. via the INT, INT3, INTO, and BOUND instructions.  Thus,
+     we set DPL==3, meaning that user programs are allowed to
+     invoke them via these instructions. */
+  intr_register_int (3, 3, INTR_ON, kill, "#BP Breakpoint Exception");
+  intr_register_int (4, 3, INTR_ON, kill, "#OF Overflow Exception");
+  intr_register_int (5, 3, INTR_ON, kill,
+                     "#BR BOUND Range Exceeded Exception");
+
+  /* These exceptions have DPL==0, preventing user processes from
+     invoking them via the INT instruction.  They can still be
+     caused indirectly, e.g. #DE can be caused by dividing by
+     0.  */
+  intr_register_int (0, 0, INTR_ON, kill, "#DE Divide Error");
+  intr_register_int (1, 0, INTR_ON, kill, "#DB Debug Exception");
+  intr_register_int (6, 0, INTR_ON, kill, "#UD Invalid Opcode Exception");
+  intr_register_int (7, 0, INTR_ON, kill,
+                     "#NM Device Not Available Exception");
+  intr_register_int (11, 0, INTR_ON, kill, "#NP Segment Not Present");
+  intr_register_int (12, 0, INTR_ON, kill, "#SS Stack Fault Exception");
+  intr_register_int (13, 0, INTR_ON, kill, "#GP General Protection Exception");
+  intr_register_int (16, 0, INTR_ON, kill, "#MF x87 FPU Floating-Point Error");
+  intr_register_int (19, 0, INTR_ON, kill,
+                     "#XF SIMD Floating-Point Exception");
+
+  /* Most exceptions can be handled with interrupts turned on.
+     We need to disable interrupts for page faults because the
+     fault address is stored in CR2 and needs to be preserved. */
+  intr_register_int (14, 0, INTR_OFF, page_fault, "#PF Page-Fault Exception");
+}
+
+/* Prints exception statistics. */
+void
+exception_print_stats (void) 
+{
+  printf ("Exception: %lld page faults\n", page_fault_cnt);
+}
+
+/* Handler for an exception (probably) caused by a user process. */
+static void
+kill (struct intr_frame *f) 
+{
+  /* This interrupt is one (probably) caused by a user process.
+     For example, the process might have tried to access unmapped
+     virtual memory (a page fault).  For now, we simply kill the
+     user process.  Later, we'll want to handle page faults in
+     the kernel.  Real Unix-like operating systems pass most
+     exceptions back to the process via signals, but we don't
+     implement them. */
+     
+  /* The interrupt frame's code segment value tells us where the
+     exception originated. */
+  switch (f->cs)
+    {
+    case SEL_UCSEG:
+      /* User's code segment, so it's a user exception, as we
+         expected.  Kill the user process.  */
+      printf ("%s: dying due to interrupt %#04x (%s).\n",
+              thread_name (), f->vec_no, intr_name (f->vec_no));
+      intr_dump_frame (f);
+      thread_exit (); 
+
+    case SEL_KCSEG:
+      /* Kernel's code segment, which indicates a kernel bug.
+         Kernel code shouldn't throw exceptions.  (Page faults
+         may cause kernel exceptions--but they shouldn't arrive
+         here.)  Panic the kernel to make the point.  */
+      intr_dump_frame (f);
+      PANIC ("Kernel bug - unexpected interrupt in kernel"); 
+
+    default:
+      /* Some other code segment?  Shouldn't happen.  Panic the
+         kernel. */
+      printf ("Interrupt %#04x (%s) in unknown segment %04x\n",
+             f->vec_no, intr_name (f->vec_no), f->cs);
+      thread_exit ();
+    }
+}
+
+/* Page fault handler.  This is a skeleton that must be filled in
+   to implement virtual memory.  Some solutions to project 2 may
+   also require modifying this code.
+
+   At entry, the address that faulted is in CR2 (Control Register
+   2) and information about the fault, formatted as described in
+   the PF_* macros in exception.h, is in F's error_code member.  The
+   example code here shows how to parse that information.  You
+   can find more information about both of these in the
+   description of "Interrupt 14--Page Fault Exception (#PF)" in
+   [IA32-v3a] section 5.15 "Exception and Interrupt Reference". */
+static void
+page_fault (struct intr_frame *f) 
+{
+  bool not_present;  /* True: not-present page, false: writing r/o page. */
+  bool write;        /* True: access was write, false: access was read. */
+  bool user;         /* True: access by user, false: access by kernel. */
+  void *fault_addr;  /* Fault address. */
+
+  /* Obtain faulting address, the virtual address that was
+     accessed to cause the fault.  It may point to code or to
+     data.  It is not necessarily the address of the instruction
+     that caused the fault (that's f->eip).
+     See [IA32-v2a] "MOV--Move to/from Control Registers" and
+     [IA32-v3a] 5.15 "Interrupt 14--Page Fault Exception
+     (#PF)". */
+  asm ("movl %%cr2, %0" : "=r" (fault_addr));
+
+#ifndef VM
+  if (!syscall_translate_vaddr(fault_addr, false))
+    thread_exit_with_return_value(f, -1);
+#endif
+
+  /* Turn interrupts back on (they were only off so that we could
+     be assured of reading CR2 before it changed). */
+  intr_enable ();
+
+  /* Count page faults. */
+  page_fault_cnt++;
+
+  /* Determine cause. */
+  not_present = (f->error_code & PF_P) == 0;
+  write = (f->error_code & PF_W) != 0;
+  user = (f->error_code & PF_U) != 0;
+
+	/* If the page fault is in the kernel, 
+		 then put the return address into EIP
+		 and return error by EAX.
+
+		 It means that the kernel can't handle the error, 
+		 so returns to the calling point and returns error.
+		 EIP: address of the next instruction to be executed.
+		 EAX: return value. */
+	// if (!user)
+	// {
+	// 	f->eip = f->eax;				
+	// 	f->eax = -1;		// EAX = 0xffffffff
+	// 	return;
+	// }
+
+#ifdef VM
+  /* To implement virtual memory, delete the rest of the function
+    body (listed above), and replace it with code that brings in the page to
+    which fault_addr refers. */
+  if(not_present && page_fault_handler(fault_addr, write, user ? f->esp : thread_current()->esp)) {
+    return; /* "Returning from page_fault() resumes the current user process" ---- FAQ */
+  }
+  else {
+    thread_exit_with_return_value(f, -1);
+  }
+#endif
+
+  /* To implement virtual memory, delete the rest of the function
+     body, and replace it with code that brings in the page to
+     which fault_addr refers. */
+  printf ("Page fault at %p: %s error %s page in %s context.\n",
+          fault_addr,
+          not_present ? "not present" : "rights violation",
+          write ? "writing" : "reading",
+          user ? "user" : "kernel");
+  kill (f);
+}
+
diff --git a/src/userprog/exception.h b/src/userprog/exception.h
new file mode 100644
index 0000000..f83e615
--- /dev/null
+++ b/src/userprog/exception.h
@@ -0,0 +1,12 @@
+#ifndef USERPROG_EXCEPTION_H
+#define USERPROG_EXCEPTION_H
+
+/* Page fault error code bits that describe the cause of the exception.  */
+#define PF_P 0x1    /* 0: not-present page. 1: access rights violation. */
+#define PF_W 0x2    /* 0: read, 1: write. */
+#define PF_U 0x4    /* 0: kernel, 1: user process. */
+
+void exception_init (void);
+void exception_print_stats (void);
+
+#endif /* userprog/exception.h */
diff --git a/src/userprog/gdt.c b/src/userprog/gdt.c
new file mode 100644
index 0000000..e866037
--- /dev/null
+++ b/src/userprog/gdt.c
@@ -0,0 +1,146 @@
+#include "userprog/gdt.h"
+#include <debug.h>
+#include "userprog/tss.h"
+#include "threads/palloc.h"
+#include "threads/vaddr.h"
+
+/* The Global Descriptor Table (GDT).
+
+   The GDT, an x86-specific structure, defines segments that can
+   potentially be used by all processes in a system, subject to
+   their permissions.  There is also a per-process Local
+   Descriptor Table (LDT) but that is not used by modern
+   operating systems.
+
+   Each entry in the GDT, which is known by its byte offset in
+   the table, identifies a segment.  For our purposes only three
+   types of segments are of interest: code, data, and TSS or
+   Task-State Segment descriptors.  The former two types are
+   exactly what they sound like.  The TSS is used primarily for
+   stack switching on interrupts.
+
+   For more information on the GDT as used here, refer to
+   [IA32-v3a] 3.2 "Using Segments" through 3.5 "System Descriptor
+   Types". */
+static uint64_t gdt[SEL_CNT];
+
+/* GDT helpers. */
+static uint64_t make_code_desc (int dpl);
+static uint64_t make_data_desc (int dpl);
+static uint64_t make_tss_desc (void *laddr);
+static uint64_t make_gdtr_operand (uint16_t limit, void *base);
+
+/* Sets up a proper GDT.  The bootstrap loader's GDT didn't
+   include user-mode selectors or a TSS, but we need both now. */
+void
+gdt_init (void)
+{
+  uint64_t gdtr_operand;
+
+  /* Initialize GDT. */
+  gdt[SEL_NULL / sizeof *gdt] = 0;
+  gdt[SEL_KCSEG / sizeof *gdt] = make_code_desc (0);
+  gdt[SEL_KDSEG / sizeof *gdt] = make_data_desc (0);
+  gdt[SEL_UCSEG / sizeof *gdt] = make_code_desc (3);
+  gdt[SEL_UDSEG / sizeof *gdt] = make_data_desc (3);
+  gdt[SEL_TSS / sizeof *gdt] = make_tss_desc (tss_get ());
+
+  /* Load GDTR, TR.  See [IA32-v3a] 2.4.1 "Global Descriptor
+     Table Register (GDTR)", 2.4.4 "Task Register (TR)", and
+     6.2.4 "Task Register".  */
+  gdtr_operand = make_gdtr_operand (sizeof gdt - 1, gdt);
+  asm volatile ("lgdt %0" : : "m" (gdtr_operand));
+  asm volatile ("ltr %w0" : : "q" (SEL_TSS));
+}
+
+/* System segment or code/data segment? */
+enum seg_class
+  {
+    CLS_SYSTEM = 0,             /* System segment. */
+    CLS_CODE_DATA = 1           /* Code or data segment. */
+  };
+
+/* Limit has byte or 4 kB page granularity? */
+enum seg_granularity
+  {
+    GRAN_BYTE = 0,              /* Limit has 1-byte granularity. */
+    GRAN_PAGE = 1               /* Limit has 4 kB granularity. */
+  };
+
+/* Returns a segment descriptor with the given 32-bit BASE and
+   20-bit LIMIT (whose interpretation depends on GRANULARITY).
+   The descriptor represents a system or code/data segment
+   according to CLASS, and TYPE is its type (whose interpretation
+   depends on the class).
+
+   The segment has descriptor privilege level DPL, meaning that
+   it can be used in rings numbered DPL or lower.  In practice,
+   DPL==3 means that user processes can use the segment and
+   DPL==0 means that only the kernel can use the segment.  See
+   [IA32-v3a] 4.5 "Privilege Levels" for further discussion. */
+static uint64_t
+make_seg_desc (uint32_t base,
+               uint32_t limit,
+               enum seg_class class,
+               int type,
+               int dpl,
+               enum seg_granularity granularity)
+{
+  uint32_t e0, e1;
+
+  ASSERT (limit <= 0xfffff);
+  ASSERT (class == CLS_SYSTEM || class == CLS_CODE_DATA);
+  ASSERT (type >= 0 && type <= 15);
+  ASSERT (dpl >= 0 && dpl <= 3);
+  ASSERT (granularity == GRAN_BYTE || granularity == GRAN_PAGE);
+
+  e0 = ((limit & 0xffff)             /* Limit 15:0. */
+        | (base << 16));             /* Base 15:0. */
+
+  e1 = (((base >> 16) & 0xff)        /* Base 23:16. */
+        | (type << 8)                /* Segment type. */
+        | (class << 12)              /* 0=system, 1=code/data. */
+        | (dpl << 13)                /* Descriptor privilege. */
+        | (1 << 15)                  /* Present. */
+        | (limit & 0xf0000)          /* Limit 16:19. */
+        | (1 << 22)                  /* 32-bit segment. */
+        | (granularity << 23)        /* Byte/page granularity. */
+        | (base & 0xff000000));      /* Base 31:24. */
+
+  return e0 | ((uint64_t) e1 << 32);
+}
+
+/* Returns a descriptor for a readable code segment with base at
+   0, a limit of 4 GB, and the given DPL. */
+static uint64_t
+make_code_desc (int dpl)
+{
+  return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 10, dpl, GRAN_PAGE);
+}
+
+/* Returns a descriptor for a writable data segment with base at
+   0, a limit of 4 GB, and the given DPL. */
+static uint64_t
+make_data_desc (int dpl)
+{
+  return make_seg_desc (0, 0xfffff, CLS_CODE_DATA, 2, dpl, GRAN_PAGE);
+}
+
+/* Returns a descriptor for an "available" 32-bit Task-State
+   Segment with its base at the given linear address, a limit of
+   0x67 bytes (the size of a 32-bit TSS), and a DPL of 0.
+   See [IA32-v3a] 6.2.2 "TSS Descriptor". */
+static uint64_t
+make_tss_desc (void *laddr)
+{
+  return make_seg_desc ((uint32_t) laddr, 0x67, CLS_SYSTEM, 9, 0, GRAN_BYTE);
+}
+
+
+/* Returns a descriptor that yields the given LIMIT and BASE when
+   used as an operand for the LGDT instruction. */
+static uint64_t
+make_gdtr_operand (uint16_t limit, void *base)
+{
+  return limit | ((uint64_t) (uint32_t) base << 16);
+}
diff --git a/src/userprog/gdt.h b/src/userprog/gdt.h
new file mode 100644
index 0000000..81fe50c
--- /dev/null
+++ b/src/userprog/gdt.h
@@ -0,0 +1,15 @@
+#ifndef USERPROG_GDT_H
+#define USERPROG_GDT_H
+
+#include "threads/loader.h"
+
+/* Segment selectors.
+   More selectors are defined by the loader in loader.h. */
+#define SEL_UCSEG       0x1B    /* User code selector. */
+#define SEL_UDSEG       0x23    /* User data selector. */
+#define SEL_TSS         0x28    /* Task-state segment. */
+#define SEL_CNT         6       /* Number of segments. */
+
+void gdt_init (void);
+
+#endif /* userprog/gdt.h */
diff --git a/src/userprog/pagedir.c b/src/userprog/pagedir.c
new file mode 100644
index 0000000..a6a87b8
--- /dev/null
+++ b/src/userprog/pagedir.c
@@ -0,0 +1,263 @@
+#include "userprog/pagedir.h"
+#include <stdbool.h>
+#include <stddef.h>
+#include <string.h>
+#include "threads/init.h"
+#include "threads/pte.h"
+#include "threads/palloc.h"
+
+static uint32_t *active_pd (void);
+static void invalidate_pagedir (uint32_t *);
+
+/* Creates a new page directory that has mappings for kernel
+   virtual addresses, but none for user virtual addresses.
+   Returns the new page directory, or a null pointer if memory
+   allocation fails. */
+uint32_t *
+pagedir_create (void) 
+{
+  uint32_t *pd = palloc_get_page (0);
+  if (pd != NULL)
+    memcpy (pd, init_page_dir, PGSIZE);
+  return pd;
+}
+
+/* Destroys page directory PD, freeing all the pages it
+   references. */
+void
+pagedir_destroy (uint32_t *pd) 
+{
+  uint32_t *pde;
+
+  if (pd == NULL)
+    return;
+
+  ASSERT (pd != init_page_dir);
+  for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++)
+    if (*pde & PTE_P) 
+      {
+        uint32_t *pt = pde_get_pt (*pde);
+        uint32_t *pte;
+        
+        for (pte = pt; pte < pt + PGSIZE / sizeof *pte; pte++)
+          if (*pte & PTE_P) 
+            palloc_free_page (pte_get_page (*pte));
+        palloc_free_page (pt);
+      }
+  palloc_free_page (pd);
+}
+
+/* Returns the address of the page table entry for virtual
+   address VADDR in page directory PD.
+   If PD does not have a page table for VADDR, behavior depends
+   on CREATE.  If CREATE is true, then a new page table is
+   created and a pointer into it is returned.  Otherwise, a null
+   pointer is returned. */
+static uint32_t *
+lookup_page (uint32_t *pd, const void *vaddr, bool create)
+{
+  uint32_t *pt, *pde;
+
+  ASSERT (pd != NULL);
+
+  /* Shouldn't create new kernel virtual mappings. */
+  ASSERT (!create || is_user_vaddr (vaddr));
+
+  /* Check for a page table for VADDR.
+     If one is missing, create one if requested. */
+  pde = pd + pd_no (vaddr);
+  if (*pde == 0) 
+    {
+      if (create)
+        {
+          pt = palloc_get_page (PAL_ZERO);
+          if (pt == NULL) 
+            return NULL; 
+      
+          *pde = pde_create (pt);
+        }
+      else
+        return NULL;
+    }
+
+  /* Return the page table entry. */
+  pt = pde_get_pt (*pde);
+  return &pt[pt_no (vaddr)];
+}
+
+/* Adds a mapping in page directory PD from user virtual page
+   UPAGE to the physical frame identified by kernel virtual
+   address KPAGE.
+   UPAGE must not already be mapped.
+   KPAGE should probably be a page obtained from the user pool
+   with palloc_get_page().
+   If WRITABLE is true, the new page is read/write;
+   otherwise it is read-only.
+   Returns true if successful, false if memory allocation
+   failed. */
+bool
+pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool writable)
+{
+  uint32_t *pte;
+
+  ASSERT (pg_ofs (upage) == 0);
+  ASSERT (pg_ofs (kpage) == 0);
+  ASSERT (is_user_vaddr (upage));
+  ASSERT (vtop (kpage) >> PTSHIFT < init_ram_pages);
+  ASSERT (pd != init_page_dir);
+
+  pte = lookup_page (pd, upage, true);
+
+  if (pte != NULL) 
+    {
+      ASSERT ((*pte & PTE_P) == 0);
+      *pte = pte_create_user (kpage, writable);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* Looks up the physical address that corresponds to user virtual
+   address UADDR in PD.  Returns the kernel virtual address
+   corresponding to that physical address, or a null pointer if
+   UADDR is unmapped. */
+void *
+pagedir_get_page (uint32_t *pd, const void *uaddr) 
+{
+  uint32_t *pte;
+
+  ASSERT (is_user_vaddr (uaddr));
+  
+  pte = lookup_page (pd, uaddr, false);
+  if (pte != NULL && (*pte & PTE_P) != 0)
+    return pte_get_page (*pte) + pg_ofs (uaddr);
+  else
+    return NULL;
+}
+
+/* Marks user virtual page UPAGE "not present" in page
+   directory PD.  Later accesses to the page will fault.  Other
+   bits in the page table entry are preserved.
+   UPAGE need not be mapped. */
+void
+pagedir_clear_page (uint32_t *pd, void *upage) 
+{
+  uint32_t *pte;
+
+  ASSERT (pg_ofs (upage) == 0);
+  ASSERT (is_user_vaddr (upage));
+
+  pte = lookup_page (pd, upage, false);
+  if (pte != NULL && (*pte & PTE_P) != 0)
+    {
+      *pte &= ~PTE_P;
+      invalidate_pagedir (pd);
+    }
+}
+
+/* Returns true if the PTE for virtual page VPAGE in PD is dirty,
+   that is, if the page has been modified since the PTE was
+   installed.
+   Returns false if PD contains no PTE for VPAGE. */
+bool
+pagedir_is_dirty (uint32_t *pd, const void *vpage) 
+{
+  uint32_t *pte = lookup_page (pd, vpage, false);
+  return pte != NULL && (*pte & PTE_D) != 0;
+}
+
+/* Set the dirty bit to DIRTY in the PTE for virtual page VPAGE
+   in PD. */
+void
+pagedir_set_dirty (uint32_t *pd, const void *vpage, bool dirty) 
+{
+  uint32_t *pte = lookup_page (pd, vpage, false);
+  if (pte != NULL) 
+    {
+      if (dirty)
+        *pte |= PTE_D;
+      else 
+        {
+          *pte &= ~(uint32_t) PTE_D;
+          invalidate_pagedir (pd);
+        }
+    }
+}
+
+/* Returns true if the PTE for virtual page VPAGE in PD has been
+   accessed recently, that is, between the time the PTE was
+   installed and the last time it was cleared.  Returns false if
+   PD contains no PTE for VPAGE. */
+bool
+pagedir_is_accessed (uint32_t *pd, const void *vpage) 
+{
+  uint32_t *pte = lookup_page (pd, vpage, false);
+  return pte != NULL && (*pte & PTE_A) != 0;
+}
+
+/* Sets the accessed bit to ACCESSED in the PTE for virtual page
+   VPAGE in PD. */
+void
+pagedir_set_accessed (uint32_t *pd, const void *vpage, bool accessed) 
+{
+  uint32_t *pte = lookup_page (pd, vpage, false);
+  if (pte != NULL) 
+    {
+      if (accessed)
+        *pte |= PTE_A;
+      else 
+        {
+          *pte &= ~(uint32_t) PTE_A; 
+          invalidate_pagedir (pd);
+        }
+    }
+}
+
+/* Loads page directory PD into the CPU's page directory base
+   register. */
+void
+pagedir_activate (uint32_t *pd) 
+{
+  if (pd == NULL)
+    pd = init_page_dir;
+
+  /* Store the physical address of the page directory into CR3
+     aka PDBR (page directory base register).  This activates our
+     new page tables immediately.  See [IA32-v2a] "MOV--Move
+     to/from Control Registers" and [IA32-v3a] 3.7.5 "Base
+     Address of the Page Directory". */
+  asm volatile ("movl %0, %%cr3" : : "r" (vtop (pd)) : "memory");
+}
+
+/* Returns the currently active page directory. */
+static uint32_t *
+active_pd (void) 
+{
+  /* Copy CR3, the page directory base register (PDBR), into
+     `pd'.
+     See [IA32-v2a] "MOV--Move to/from Control Registers" and
+     [IA32-v3a] 3.7.5 "Base Address of the Page Directory". */
+  uintptr_t pd;
+  asm volatile ("movl %%cr3, %0" : "=r" (pd));
+  return ptov (pd);
+}
+
+/* Seom page table changes can cause the CPU's translation
+   lookaside buffer (TLB) to become out-of-sync with the page
+   table.  When this happens, we have to "invalidate" the TLB by
+   re-activating it.
+
+   This function invalidates the TLB if PD is the active page
+   directory.  (If PD is not active then its entries are not in
+   the TLB, so there is no need to invalidate anything.) */
+static void
+invalidate_pagedir (uint32_t *pd) 
+{
+  if (active_pd () == pd) 
+    {
+      /* Re-activating PD clears the TLB.  See [IA32-v3a] 3.12
+         "Translation Lookaside Buffers (TLBs)". */
+      pagedir_activate (pd);
+    } 
+}
diff --git a/src/userprog/pagedir.h b/src/userprog/pagedir.h
new file mode 100644
index 0000000..cd92447
--- /dev/null
+++ b/src/userprog/pagedir.h
@@ -0,0 +1,18 @@
+#ifndef USERPROG_PAGEDIR_H
+#define USERPROG_PAGEDIR_H
+
+#include <stdbool.h>
+#include <stdint.h>
+
+uint32_t *pagedir_create (void);
+void pagedir_destroy (uint32_t *pd);
+bool pagedir_set_page (uint32_t *pd, void *upage, void *kpage, bool rw);
+void *pagedir_get_page (uint32_t *pd, const void *upage);
+void pagedir_clear_page (uint32_t *pd, void *upage);
+bool pagedir_is_dirty (uint32_t *pd, const void *upage);
+void pagedir_set_dirty (uint32_t *pd, const void *upage, bool dirty);
+bool pagedir_is_accessed (uint32_t *pd, const void *upage);
+void pagedir_set_accessed (uint32_t *pd, const void *upage, bool accessed);
+void pagedir_activate (uint32_t *pd);
+
+#endif /* userprog/pagedir.h */
diff --git a/src/userprog/process.c b/src/userprog/process.c
new file mode 100644
index 0000000..3eff76d
--- /dev/null
+++ b/src/userprog/process.c
@@ -0,0 +1,700 @@
+#include "userprog/process.h"
+#include <debug.h>
+#include <inttypes.h>
+#include <round.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "userprog/gdt.h"
+#include "userprog/pagedir.h"
+#include "userprog/tss.h"
+#include "filesys/directory.h"
+#include "filesys/file.h"
+#include "filesys/filesys.h"
+#include "threads/flags.h"
+#include "threads/init.h"
+#include "threads/interrupt.h"
+#include "threads/palloc.h"
+#include "threads/thread.h"
+#include "threads/vaddr.h"
+
+#ifdef VM
+#include "threads/malloc.h"
+#include <vm/frame.h>
+#include "vm/page.h"
+#include "userprog/syscall.h"
+#endif
+
+static thread_func start_process NO_RETURN;
+static bool load (const char *cmdline, void (**eip) (void), void **esp);
+
+/* Starts a new thread running a user program loaded from
+   FILENAME.  The new thread may be scheduled (and may even exit)
+   before process_execute() returns.  Returns the new process's
+   thread id, or TID_ERROR if the thread cannot be created. */
+tid_t
+process_execute (const char *file_name) 
+{
+  char *fn_copy;
+  char *fn_tmp;
+  tid_t tid;
+
+  fn_tmp = palloc_get_page(0);
+  if (fn_tmp == NULL)
+    return TID_ERROR;
+
+  /* Make a copy of FILE_NAME.
+     Otherwise there's a race between the caller and load(). */
+  fn_copy = palloc_get_page (0);
+  if (fn_copy == NULL)
+  {
+    palloc_free_page(fn_tmp);
+    return TID_ERROR;
+  }
+    
+  strlcpy (fn_copy, file_name, PGSIZE);
+  strlcpy (fn_tmp, file_name, PGSIZE);
+
+  char *remain_name;
+  char *real_name = strtok_r(fn_tmp, " ", &remain_name);
+
+
+  /* Create a new thread to execute FILE_NAME. */
+  tid = thread_create (real_name, PRI_DEFAULT, start_process, fn_copy);
+  palloc_free_page(fn_tmp);
+  if (tid == TID_ERROR)
+  {
+    palloc_free_page (fn_copy);
+    return tid; 
+  }
+
+  sema_down(&thread_current()->sema_waiting);
+  if (!thread_current()->child_success) return TID_ERROR;//can't create new process thread,return error
+  return tid;
+}
+
+/* A thread function that loads a user process and starts it
+   running. */
+static void
+start_process (void *file_name_)
+{
+  char *file_name = file_name_;
+  struct intr_frame if_;
+  bool success;
+
+  /* Initialize interrupt frame and load executable. */
+  memset (&if_, 0, sizeof if_);
+  if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG;
+  if_.cs = SEL_UCSEG;
+  if_.eflags = FLAG_IF | FLAG_MBS;
+
+  char *fn_copy = malloc(strlen(file_name)+1);  //Get the copy of file_name
+  strlcpy(fn_copy,file_name,strlen(file_name)+1);
+
+  char *remain_name;
+  file_name = strtok_r(file_name, " ", &remain_name);  //Only need to send file_name to load.
+  success = load (file_name, &if_.eip, &if_.esp);
+
+    /* Set up stack */
+  char *token;
+  if (success)
+  {
+    char *record_para[100];
+    int argv[100]; // Address of parameters in the stack
+    int argc = 0;
+
+    for (token = strtok_r (fn_copy, " ", &remain_name); token != NULL; 
+         token = strtok_r (NULL, " ", &remain_name)){
+      record_para[argc] = token;
+      argc++;
+    }
+
+    for(int i = argc-1; i >= 0; i--)
+    {
+      if_.esp -= (strlen(record_para[i]) + 1);
+      memcpy (if_.esp, record_para[i], strlen(record_para[i])+1);
+      argv[i] = (int) if_.esp;
+    }
+
+    push_address (&if_.esp, argc, argv);
+
+    thread_current ()->parent_thread->child_success = true;
+    sema_up (&thread_current ()->parent_thread->sema_waiting);
+  }
+  free(fn_copy);
+
+  /* If load failed, quit. */
+  palloc_free_page (file_name);
+  if (!success)
+  {
+    thread_current ()->parent_thread->child_success = false;
+    sema_up (&thread_current ()->parent_thread->sema_waiting);
+    thread_exit ();
+  }
+
+  /* Start the user process by simulating a return from an
+     interrupt, implemented by intr_exit (in
+     threads/intr-stubs.S).  Because intr_exit takes all of its
+     arguments on the stack in the form of a `struct intr_frame',
+     we just point the stack pointer (%esp) to our stack frame
+     and jump to it. */
+  asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory");
+  NOT_REACHED ();
+}
+
+
+/* Push parameters' addresses into stack */
+void
+push_address (void **esp, int argc, int argv[]){
+  *esp = (int)*esp & 0xfffffffc;
+  *esp -= 4;
+  *(int *) *esp = 0;
+
+  for (int i = argc - 1; i >= 0; i--)
+  {
+    *esp -= 4;
+    *(int *) *esp = argv[i];
+  }
+  *esp -= 4;
+  *(int *) *esp = (int) *esp + 4;
+  *esp -= 4;
+  *(int *) *esp = argc;
+  *esp -= 4;
+  *(int *) *esp = 0;
+}
+
+/* Waits for thread TID to die and returns its exit status.  If
+   it was terminated by the kernel (i.e. killed due to an
+   exception), returns -1.  If TID is invalid or if it was not a
+   child of the calling process, or if process_wait() has already
+   been successfully called for the given TID, returns -1
+   immediately, without waiting.
+
+   This function will be implemented in problem 2-2.  For now, it
+   does nothing. */
+int
+process_wait (tid_t child_tid UNUSED) 
+{
+  /* Find the child's ID that the current thread waits for 
+     and sema down the child's semaphore */
+  struct list *children = &thread_current ()->children_list;
+  struct list_elem *child_elem_ptr;
+  child_elem_ptr = list_begin (children);
+  struct child *child_ptr = NULL;
+  while (child_elem_ptr != list_end (children))
+  {
+    /* Convert child_elem into a pointer to child thread. */
+    child_ptr = list_entry (child_elem_ptr, struct child, child_elem);
+    if (child_ptr->tid == child_tid)
+    {
+      if (!child_ptr->has_been_waited)   
+      { 
+        /* If it is the first time for parent to call wait on this child, then wait. */
+        child_ptr->has_been_waited = true;
+        sema_down (&child_ptr->sema_waiting);
+        break;
+      } 
+      else
+      {
+        /* Otherwise, return -1 immediately without waiting. */
+        return -1;
+      }
+    }
+    child_elem_ptr = list_next (child_elem_ptr);
+  }
+  /* Return -1 if can't find, which means TID is invalid or if it was not a
+     child of the calling process. */
+  if (child_elem_ptr == list_end (children))
+  {
+    return -1;
+  }
+
+  /* Now the child process ends, remove this child from 
+     children_list and returns its exit status we stored. */
+  list_remove (child_elem_ptr);
+  return child_ptr->stored_exit_status;
+}
+
+/* Free the current process's resources. */
+void
+process_exit (void)
+{
+  struct thread *cur = thread_current ();
+  uint32_t *pd;
+
+#ifdef VM
+  struct list* mmap_list = &cur->mmap_file_list;
+  if (!list_empty(mmap_list))
+  {
+    struct mmap_file* mh;
+    while (!list_empty(mmap_list))
+    {
+      mh = list_entry(list_pop_front (mmap_list), struct mmap_file, elem);
+      for (int i = 0; i < mh->num_page_with_segment; i++)
+      {
+        page_unmap(cur->page_table, mh->mmap_addr + i * PGSIZE);
+      }
+      delete_mmap_handle(mh);
+    }
+  }
+  page_destroy(cur->page_table);
+  
+#endif
+
+  /* Destroy the current process's page directory and switch back
+     to the kernel-only page directory. */
+  pd = cur->pagedir;
+  if (pd != NULL) 
+    {
+      /* Correct ordering here is crucial.  We must set
+         cur->pagedir to NULL before switching page directories,
+         so that a timer interrupt can't switch back to the
+         process page directory.  We must activate the base page
+         directory before destroying the process's page
+         directory, or our active page directory will be one
+         that's been freed (and cleared). */
+      cur->pagedir = NULL;
+      pagedir_activate (NULL);
+      pagedir_destroy (pd);
+    }
+}
+
+/* Sets up the CPU for running user code in the current
+   thread.
+   This function is called on every context switch. */
+void
+process_activate (void)
+{
+  struct thread *t = thread_current ();
+
+  /* Activate thread's page tables. */
+  pagedir_activate (t->pagedir);
+
+  /* Set thread's kernel stack for use in processing
+     interrupts. */
+  tss_update ();
+}
+
+/* We load ELF binaries.  The following definitions are taken
+   from the ELF specification, [ELF1], more-or-less verbatim.  */
+
+/* ELF types.  See [ELF1] 1-2. */
+typedef uint32_t Elf32_Word, Elf32_Addr, Elf32_Off;
+typedef uint16_t Elf32_Half;
+
+/* For use with ELF types in printf(). */
+#define PE32Wx PRIx32   /* Print Elf32_Word in hexadecimal. */
+#define PE32Ax PRIx32   /* Print Elf32_Addr in hexadecimal. */
+#define PE32Ox PRIx32   /* Print Elf32_Off in hexadecimal. */
+#define PE32Hx PRIx16   /* Print Elf32_Half in hexadecimal. */
+
+/* Executable header.  See [ELF1] 1-4 to 1-8.
+   This appears at the very beginning of an ELF binary. */
+struct Elf32_Ehdr
+  {
+    unsigned char e_ident[16];
+    Elf32_Half    e_type;
+    Elf32_Half    e_machine;
+    Elf32_Word    e_version;
+    Elf32_Addr    e_entry;
+    Elf32_Off     e_phoff;
+    Elf32_Off     e_shoff;
+    Elf32_Word    e_flags;
+    Elf32_Half    e_ehsize;
+    Elf32_Half    e_phentsize;
+    Elf32_Half    e_phnum;
+    Elf32_Half    e_shentsize;
+    Elf32_Half    e_shnum;
+    Elf32_Half    e_shstrndx;
+  };
+
+/* Program header.  See [ELF1] 2-2 to 2-4.
+   There are e_phnum of these, starting at file offset e_phoff
+   (see [ELF1] 1-6). */
+struct Elf32_Phdr
+  {
+    Elf32_Word p_type;
+    Elf32_Off  p_offset;
+    Elf32_Addr p_vaddr;
+    Elf32_Addr p_paddr;
+    Elf32_Word p_filesz;
+    Elf32_Word p_memsz;
+    Elf32_Word p_flags;
+    Elf32_Word p_align;
+  };
+
+/* Values for p_type.  See [ELF1] 2-3. */
+#define PT_NULL    0            /* Ignore. */
+#define PT_LOAD    1            /* Loadable segment. */
+#define PT_DYNAMIC 2            /* Dynamic linking info. */
+#define PT_INTERP  3            /* Name of dynamic loader. */
+#define PT_NOTE    4            /* Auxiliary info. */
+#define PT_SHLIB   5            /* Reserved. */
+#define PT_PHDR    6            /* Program header table. */
+#define PT_STACK   0x6474e551   /* Stack segment. */
+
+/* Flags for p_flags.  See [ELF3] 2-3 and 2-4. */
+#define PF_X 1          /* Executable. */
+#define PF_W 2          /* Writable. */
+#define PF_R 4          /* Readable. */
+
+static bool setup_stack (void **esp);
+static bool validate_segment (const struct Elf32_Phdr *, struct file *);
+static bool load_segment (struct file *file, off_t ofs, uint8_t *upage,
+                          uint32_t read_bytes, uint32_t zero_bytes,
+                          bool writable);
+
+/* Loads an ELF executable from FILE_NAME into the current thread.
+   Stores the executable's entry point into *EIP
+   and its initial stack pointer into *ESP.
+   Returns true if successful, false otherwise. */
+bool
+load (const char *file_name, void (**eip) (void), void **esp)
+{
+
+  struct thread *t = thread_current ();
+  struct Elf32_Ehdr ehdr;
+  struct file *file = NULL;
+  off_t file_ofs;
+  bool success = false;
+  int i;
+
+  #ifdef VM
+  t->page_table = page_create();
+  if(t->page_table == NULL)
+  	goto done;
+  #endif
+
+  /* Allocate and activate page directory. */
+  t->pagedir = pagedir_create ();
+  if (t->pagedir == NULL)
+    goto done;
+  process_activate ();
+
+  /* Open executable file. */
+  acquire_lock_file();
+  file = filesys_open (file_name);
+  if (file == NULL)
+  {
+    printf ("load: %s: open failed\n", file_name);
+    goto done;
+  }
+  /* Deny write for the opened file by calling file deny write */
+  file_deny_write(file);
+  t->file_owned = file;
+  /* Read and verify executable header. */
+  if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr
+      || memcmp (ehdr.e_ident, "\177ELF\1\1\1", 7)
+      || ehdr.e_type != 2
+      || ehdr.e_machine != 3
+      || ehdr.e_version != 1
+      || ehdr.e_phentsize != sizeof (struct Elf32_Phdr)
+      || ehdr.e_phnum > 1024)
+    {
+      printf ("load: %s: error loading executable\n", file_name);
+      goto done;
+    }
+
+  /* Read program headers. */
+  file_ofs = ehdr.e_phoff;
+  for (i = 0; i < ehdr.e_phnum; i++)
+    {
+      struct Elf32_Phdr phdr;
+
+      if (file_ofs < 0 || file_ofs > file_length (file))
+        goto done;
+      file_seek (file, file_ofs);
+
+      if (file_read (file, &phdr, sizeof phdr) != sizeof phdr)
+        goto done;
+      file_ofs += sizeof phdr;
+      switch (phdr.p_type)
+        {
+        case PT_NULL:
+        case PT_NOTE:
+        case PT_PHDR:
+        case PT_STACK:
+        default:
+          /* Ignore this segment. */
+          break;
+        case PT_DYNAMIC:
+        case PT_INTERP:
+        case PT_SHLIB:
+          goto done;
+        case PT_LOAD:
+          if (validate_segment (&phdr, file))
+            {
+              bool writable = (phdr.p_flags & PF_W) != 0;
+              uint32_t file_page = phdr.p_offset & ~PGMASK;
+              uint32_t mem_page = phdr.p_vaddr & ~PGMASK;
+              uint32_t page_offset = phdr.p_vaddr & PGMASK;
+              uint32_t read_bytes, zero_bytes;
+              if (phdr.p_filesz > 0)
+                {
+                  /* Normal segment.
+                     Read initial part from disk and zero the rest. */
+                  read_bytes = page_offset + phdr.p_filesz;
+                  zero_bytes = (ROUND_UP (page_offset + phdr.p_memsz, PGSIZE)
+                                - read_bytes);
+                }
+              else
+                {
+                  /* Entirely zero.
+                     Don't read anything from disk. */
+                  read_bytes = 0;
+                  zero_bytes = ROUND_UP (page_offset + phdr.p_memsz, PGSIZE);
+                }
+              if (!load_segment (file, file_page, (void *) mem_page,
+                                 read_bytes, zero_bytes, writable))
+                goto done;
+            }
+          else
+            goto done;
+          break;
+        }
+    }
+
+
+  /* Set up stack. */
+  if (!setup_stack (esp))
+    goto done;
+
+  /* Start address. */
+  *eip = (void (*) (void)) ehdr.e_entry;
+
+  success = true;
+
+ done:
+  /* We arrive here whether the load is successful or not. */
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+  if (success)
+  {
+    t->file_owned = file;
+#ifdef FILESYS
+    t->current_dir = get_file_dir(file);
+#endif
+    file_deny_write(file);
+  }
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+  release_lock_file();
+  return success;
+}
+/* load() helpers. */
+
+static bool install_page (void *upage, void *kpage, bool writable);
+
+/* Checks whether PHDR describes a valid, loadable segment in
+   FILE and returns true if so, false otherwise. */
+static bool
+validate_segment (const struct Elf32_Phdr *phdr, struct file *file) 
+{
+  /* p_offset and p_vaddr must have the same page offset. */
+  if ((phdr->p_offset & PGMASK) != (phdr->p_vaddr & PGMASK)) 
+    return false; 
+
+  /* p_offset must point within FILE. */
+  if (phdr->p_offset > (Elf32_Off) file_length (file)) 
+    return false;
+
+  /* p_memsz must be at least as big as p_filesz. */
+  if (phdr->p_memsz < phdr->p_filesz) 
+    return false; 
+
+  /* The segment must not be empty. */
+  if (phdr->p_memsz == 0)
+    return false;
+  
+  /* The virtual memory region must both start and end within the
+     user address space range. */
+  if (!is_user_vaddr ((void *) phdr->p_vaddr))
+    return false;
+  if (!is_user_vaddr ((void *) (phdr->p_vaddr + phdr->p_memsz)))
+    return false;
+
+  /* The region cannot "wrap around" across the kernel virtual
+     address space. */
+  if (phdr->p_vaddr + phdr->p_memsz < phdr->p_vaddr)
+    return false;
+
+  /* Disallow mapping page 0.
+     Not only is it a bad idea to map page 0, but if we allowed
+     it then user code that passed a null pointer to system calls
+     could quite likely panic the kernel by way of null pointer
+     assertions in memcpy(), etc. */
+  if (phdr->p_vaddr < PGSIZE)
+    return false;
+
+  /* It's okay. */
+  return true;
+}
+
+/* Loads a segment starting at offset OFS in FILE at address
+   UPAGE.  In total, READ_BYTES + ZERO_BYTES bytes of virtual
+   memory are initialized, as follows:
+
+        - READ_BYTES bytes at UPAGE must be read from FILE
+          starting at offset OFS.
+
+        - ZERO_BYTES bytes at UPAGE + READ_BYTES must be zeroed.
+
+   The pages initialized by this function must be writable by the
+   user process if WRITABLE is true, read-only otherwise.
+
+   Return true if successful, false if a memory allocation error
+   or disk read error occurs. */
+static bool
+load_segment (struct file *file, off_t ofs, uint8_t *upage,
+              uint32_t read_bytes, uint32_t zero_bytes, bool writable) 
+{
+  ASSERT ((read_bytes + zero_bytes) % PGSIZE == 0);
+  ASSERT (pg_ofs (upage) == 0);
+  ASSERT (ofs % PGSIZE == 0);
+
+#ifdef VM
+  return mmap_load_segment(file, ofs, upage, read_bytes, zero_bytes, writable);
+#else
+
+  file_seek (file, ofs);
+  while (read_bytes > 0 || zero_bytes > 0) 
+    {
+      /* Calculate how to fill this page.
+         We will read PAGE_READ_BYTES bytes from FILE
+         and zero the final PAGE_ZERO_BYTES bytes. */
+      size_t page_read_bytes = read_bytes < PGSIZE ? read_bytes : PGSIZE;
+      size_t page_zero_bytes = PGSIZE - page_read_bytes;
+
+      /* Get a page of memory. */
+      uint8_t *kpage = palloc_get_page (PAL_USER);
+      if (kpage == NULL)
+        return false;
+
+      /* Load this page. */
+      if (file_read (file, kpage, page_read_bytes) != (int) page_read_bytes)
+        {
+          palloc_free_page (kpage);
+          return false; 
+        }
+      memset (kpage + page_read_bytes, 0, page_zero_bytes);
+
+      /* Add the page to the process's address space. */
+      if (!install_page (upage, kpage, writable)) 
+        {
+          palloc_free_page (kpage);
+          return false; 
+        }
+
+      /* Advance. */
+      read_bytes -= page_read_bytes;
+      zero_bytes -= page_zero_bytes;
+      upage += PGSIZE;
+    }
+  return true;
+  
+#endif
+}
+
+/* Create a minimal stack by mapping a zeroed page at the top of
+   user virtual memory. */
+static bool
+setup_stack (void **esp) 
+{
+  uint8_t *kpage;
+  bool success = false;
+
+#ifdef VM
+    kpage = frame_get_frame(PAL_USER | PAL_ZERO, ((uint8_t *) PHYS_BASE) - PGSIZE);
+#else
+    kpage = palloc_get_page (PAL_USER | PAL_ZERO);
+#endif
+
+  if (kpage != NULL) 
+  {
+    success = install_page (((uint8_t *) PHYS_BASE) - PGSIZE, kpage, true);
+    if (success)
+      *esp = PHYS_BASE;
+    else
+#ifdef VM
+      frame_free_frame(kpage);
+#else
+      palloc_free_page (kpage);
+#endif
+  }
+#ifdef VM
+  if (success)
+    frame_set_pinned_false(kpage);
+#endif
+  return success;
+}
+
+/* Adds a mapping from user virtual address UPAGE to kernel
+   virtual address KPAGE to the page table.
+   If WRITABLE is true, the user process may modify the page;
+   otherwise, it is read-only.
+   UPAGE must not already be mapped.
+   KPAGE should probably be a page obtained from the user pool
+   with palloc_get_page().
+   Returns true on success, false if UPAGE is already mapped or
+   if memory allocation fails. */
+static bool
+install_page (void *upage, void *kpage, bool writable)
+{
+#ifdef VM
+  return page_set_frame(upage, kpage, writable);
+#else
+  struct thread *t = thread_current ();
+  /* Verify that there's not already a page at that virtual
+     address, then map our page there. */
+  return (pagedir_get_page (t->pagedir, upage) == NULL
+          && pagedir_set_page (t->pagedir, upage, kpage, writable));
+#endif
+}
+
+
+struct mmap_file*
+syscall_get_mmap_handle(mapid_t mapid)
+{
+#ifdef VM
+  struct thread* cur = thread_current();
+  struct list_elem *i;
+  struct mmap_file *mh;
+  if (!list_empty(&cur->mmap_file_list))
+  {
+    for(i = list_begin(&cur->mmap_file_list); i != list_end(&cur->mmap_file_list); i = list_next(i))
+    {
+      mh = list_entry(i, struct mmap_file, elem);
+      if (mh->mapid == mapid)
+        return mh;
+    }
+  }
+  return NULL;
+#endif
+}
+
+bool
+delete_mmap_handle(struct mmap_file *mh)
+{
+#ifdef VM
+  struct thread* cur = thread_current();
+  struct list_elem *i;
+  struct mmap_file *tmp_mh;
+  if (!list_empty(&cur->mmap_file_list))
+  {
+    for(i = list_begin(&cur->mmap_file_list); i != list_end(&cur->mmap_file_list); i = list_next(i))
+    {
+      tmp_mh = list_entry(i, struct mmap_file, elem);
+      if (tmp_mh == mh)
+      {
+        list_remove (i);
+        acquire_lock_file ();
+        file_close (mh->mmap_file);
+        release_lock_file ();
+        free (mh);
+        return true;
+      }
+    }
+  }
+  return false;
+#endif
+}
diff --git a/src/userprog/process.h b/src/userprog/process.h
new file mode 100644
index 0000000..be7b412
--- /dev/null
+++ b/src/userprog/process.h
@@ -0,0 +1,15 @@
+#ifndef USERPROG_PROCESS_H
+#define USERPROG_PROCESS_H
+
+#include "threads/thread.h"
+
+tid_t process_execute (const char *file_name);
+int process_wait (tid_t);
+void process_exit (void);
+void process_activate (void);
+void push_address (void **esp, int argc, int argv[]);
+
+struct mmap_file* syscall_get_mmap_handle(mapid_t mapid);
+bool delete_mmap_handle(struct mmap_file *mh);
+
+#endif /* userprog/process.h */
diff --git a/src/userprog/syscall.c b/src/userprog/syscall.c
new file mode 100644
index 0000000..48054ab
--- /dev/null
+++ b/src/userprog/syscall.c
@@ -0,0 +1,1108 @@
+#include "userprog/syscall.h"
+#include <stdio.h>
+#include <stdint.h>
+#include <syscall-nr.h>
+#include "threads/interrupt.h"
+#include "threads/thread.h"
+
+#include <filesys/file.h>
+#include "filesys/filesys.h"
+#include "threads/vaddr.h"
+#include "userprog/pagedir.h"
+#include "devices/shutdown.h"
+#include "threads/synch.h"
+#include "userprog/process.h"
+
+#include "lib/user/syscall.h"
+#ifdef VM
+#include "vm/page.h"
+#endif
+#ifdef FILESYS
+#include "filesys/directory.h"
+#include "filesys/inode.h"
+#endif
+
+static void syscall_handler (struct intr_frame *);
+
+static int get_user(const uint8_t *);
+
+static void syscall_halt (struct intr_frame *f);
+static void syscall_exit (struct intr_frame *f, const int status);
+static void syscall_exec (struct intr_frame *f, const char *file_name);
+static void syscall_wait (struct intr_frame *f, pid_t pid);
+static void syscall_create (struct intr_frame *f, const char *file_name, unsigned initial_size);
+static void syscall_remove (struct intr_frame *f, const char *file_name);
+static void syscall_open (struct intr_frame *f, const char *file_name);
+static void syscall_filesize (struct intr_frame *f, int fd);
+static void syscall_read (struct intr_frame *f, int fd, void *buffer, unsigned size);
+static void syscall_write (struct intr_frame* f, int fd, const void* buffer, unsigned size);
+static void syscall_seek (struct intr_frame *f, int fd, unsigned offset);
+static void syscall_tell (struct intr_frame *f, int fd);
+static void syscall_close (struct intr_frame *f, int fd);
+
+/* ----- Newly Add for Proj03 VM ----- */
+static void syscall_mmap(struct intr_frame *f, int fd, const void *obj_vaddr);
+static void syscall_munmap(struct intr_frame *f, mapid_t mapid);
+/* ----- Newly Add for Proj03 VM ----- */
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+static void syscall_chdir(struct intr_frame *f, const char *dir);
+static void syscall_mkdir(struct intr_frame *f, const char *dir);
+static void syscall_readdir(struct intr_frame *f, int fd, char *name);
+static void syscall_isdir(struct intr_frame *f, int fd);
+static void syscall_inumber(struct intr_frame *f, int fd);
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+
+struct lock file_lock;
+
+void
+syscall_init (void) 
+{
+  intr_register_int (0x30, 3, INTR_ON, syscall_handler, "syscall");
+  lock_init(&file_lock);
+}
+
+static void
+syscall_handler (struct intr_frame *f UNUSED) 
+{
+
+/* Restore the current thread's stack pointer (esp) to the value saved 
+   in the interrupt frame during a system call, ensuring proper stack 
+   management and dynamic stack growth. */
+#ifdef VM
+  thread_current ()->esp = f->esp;
+#endif
+
+  /* Check the first parameter. */
+  if (!is_valid_uptr (f->esp))
+  {
+    exit_handler (f, -1);
+  }
+
+  /* Extract the system call number. */
+  int syscall_num = *((int *)f->esp);
+
+  /* Set parameter(s) according to the corresponding handler function. */
+  void *arg1, *arg2, *arg3;
+  switch (syscall_num)
+  {
+    case SYS_EXIT:
+    case SYS_EXEC:
+    case SYS_WAIT:
+    case SYS_REMOVE:
+    case SYS_OPEN:
+    case SYS_FILESIZE:
+    case SYS_TELL:
+    case SYS_CLOSE:
+    case SYS_MUNMAP:
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+    case SYS_CHDIR:
+    case SYS_MKDIR:
+    case SYS_ISDIR:
+    case SYS_INUMBER:
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+      if (!is_valid_uptr (f->esp + 4))
+      {
+        exit_handler (f, -1);
+      }
+      arg1 = f->esp + 4;
+      break;
+
+    case SYS_CREATE:
+    case SYS_SEEK:
+    case SYS_MMAP:
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+    case SYS_READDIR:
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+      if (!is_valid_uptr (f->esp + 8))
+      {
+        exit_handler (f, -1);
+      }
+      arg1 = f->esp + 4;
+      arg2 = f->esp + 8;
+      break;
+
+    case SYS_READ:
+    case SYS_WRITE:
+      if (!is_valid_uptr (f->esp + 12))
+      {
+        exit_handler (f, -1);
+      }
+      arg1 = f->esp + 4;
+      arg2 = f->esp + 8;
+      arg3 = f->esp + 12;
+      break;
+
+    default: break;
+  }
+
+  /* Dispatch the call to the corresponding handler function. */
+  switch (syscall_num)
+  {
+    case SYS_HALT:
+      syscall_halt (f);
+      break;
+
+    case SYS_EXIT:
+      syscall_exit (f, *((int *) arg1));
+      break;
+
+    case SYS_EXEC:
+      syscall_exec (f, *((char **) arg1));
+      break;
+
+    case SYS_WAIT:
+      syscall_wait (f, *((pid_t *) arg1));
+      break;
+    
+    case SYS_REMOVE:
+      syscall_remove (f, *((char **) arg1));
+      break;
+
+    case SYS_OPEN:
+      syscall_open (f, *((char **) arg1));
+      break;
+
+    case SYS_FILESIZE:
+      syscall_filesize (f, *((int *) arg1));
+      break;
+
+    case SYS_TELL:
+      syscall_tell (f, *((int *) arg1));
+      break;
+
+    case SYS_CLOSE:
+      syscall_close (f, *((int *) arg1));
+      break;
+
+    case SYS_MUNMAP:
+      syscall_munmap(f, *((mapid_t *) arg1));
+      break;
+
+    case SYS_CREATE:
+      syscall_create (f, *((char **) arg1), *((unsigned *) arg2));
+      break;
+
+    case SYS_SEEK:
+      syscall_seek (f, *((int *) arg1), *((unsigned *) arg2));
+      break;
+      
+    case SYS_MMAP:
+      syscall_mmap(f, *((int *) arg1), *((void **) arg2));
+      break;
+
+    case SYS_READ:
+      syscall_read (f, *((int *) arg1), *((char **) arg2), 
+                    *((unsigned *) arg3));
+      break;
+
+    case SYS_WRITE:
+      syscall_write (f, *((int *) arg1), *((char **) arg2),
+                    *((unsigned *) arg3));
+      break;
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+#ifdef FILESYS
+    case SYS_CHDIR:
+      syscall_chdir(f, *((char **) arg1));
+      break;
+
+    case SYS_MKDIR:
+      syscall_mkdir(f, *((char **) arg1));
+      break;
+
+    case SYS_READDIR:
+      syscall_readdir(f, *((int *) arg1), *((char **) arg2));
+      break;
+
+    case SYS_ISDIR:
+      syscall_isdir(f, *((int *) arg1));
+      break;
+
+    case SYS_INUMBER:
+      syscall_inumber(f, *((int *) arg1));
+      break;
+#endif
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+
+    default:
+      exit_handler (f, -1);
+  }
+}
+
+void 
+syscall_halt (struct intr_frame *f)
+{
+  shutdown_power_off ();
+}
+
+void 
+syscall_exit (struct intr_frame *f, const int status)
+{
+  exit_handler (f, status);
+}
+
+/* Terminates the current user program with given status.
+   Report error if necessary. */
+void 
+exit_handler (struct intr_frame *f, int status) {
+  thread_current ()->exit_status = status;
+  f->eax = (uint32_t)status;
+  thread_exit();
+}
+
+void 
+syscall_exec (struct intr_frame *f, const char *file_name)
+{
+  if (!is_valid_string (file_name))
+  {
+    exit_handler (f, -1);
+  }
+  lock_acquire (&file_lock);
+  f->eax = process_execute (file_name);
+  lock_release (&file_lock);
+}
+
+void 
+syscall_wait (struct intr_frame *f, pid_t pid)
+{
+  f->eax = (uint32_t) process_wait (pid);
+}
+
+void 
+syscall_create (struct intr_frame *f, const char *file_name, unsigned initial_size)
+{
+  if(!is_valid_string (file_name))
+  {
+    exit_handler (f, -1);
+  }
+  acquire_lock_file ();   // File operating needs lock.
+  f->eax = filesys_create (file_name, initial_size);
+  release_lock_file ();
+}
+
+void 
+syscall_remove (struct intr_frame *f, const char *file_name)
+{
+  if (!is_valid_string (file_name))
+  {
+    exit_handler (f, -1);
+  }
+  acquire_lock_file ();
+  f->eax = filesys_remove (file_name);
+  release_lock_file ();
+}
+
+void 
+syscall_open (struct intr_frame *f, const char *file_name)
+{
+  if (!is_valid_string (file_name))
+  {
+    exit_handler (f, -1);
+  }
+
+  uint32_t *user_ptr = f->esp;
+  *user_ptr++;
+  acquire_lock_file ();
+  struct file *file_opened = filesys_open ((const char *)*user_ptr);
+  release_lock_file ();
+
+  /* If the file can not be opened, return error, but not exit. */
+  if (file_opened == NULL)
+  {
+    f->eax = (uint32_t)-1;
+    return;
+  }
+
+  struct thread * t = thread_current();
+
+  /* If a file is opened by the thread, record its info by a new struct thread_file. */
+  struct thread_file *thread_file_temp = malloc (sizeof (struct thread_file));
+  thread_file_temp->file = file_opened;
+  thread_file_temp->owned_thread = thread_current();
+
+  /* Encode the newly opened file with fd, then update fd of the thread, 
+     ensuring that each file handled by the thread
+     gets a unique file descriptor. */
+  thread_file_temp->fd = t->max_file_fd++;
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+#ifdef FILESYS
+  if (inode_isdir(file_get_inode(file_opened)))
+    thread_file_temp->opened_dir = dir_open(inode_reopen(file_get_inode(file_opened)));
+#endif
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+  list_push_back (&t->files_list, &thread_file_temp->file_elem);
+
+  f->eax = thread_file_temp->fd;
+}
+
+void 
+syscall_filesize (struct intr_frame *f, int fd)
+{
+  struct thread_file *thread_file_temp = find_file_by_fd (fd);
+  if (thread_file_temp)
+  {
+    /* Find the file, then return the size in bytes. */
+    acquire_lock_file ();
+    f->eax = file_length (thread_file_temp->file);
+    release_lock_file ();
+  } 
+  else
+  {
+    /* Otherwise, return error. */
+    exit_handler(f, -1);
+  }
+}
+
+void 
+syscall_read (struct intr_frame *f, int fd, void *buffer, unsigned size)
+{
+  /* Check whole buffer, need to write to the buffer. */
+  if (!is_vaild_buffer(buffer, size, true))
+  {
+    exit_handler (f, -1);
+  }
+
+  if (fd == STDOUT_FILENO)
+  {
+    exit_handler (f, -1);
+  }
+  uint8_t *read_buffer = (uint8_t *)buffer;
+  if (fd == STDIN_FILENO)
+  {
+    /* Reads from the keyboard. */
+    for (int i = 0; i < size; i++)
+      read_buffer[i] = input_getc();
+    f->eax = size;
+  }
+  else
+  {
+    /* Read from the file. */
+    struct thread_file *thread_file_temp = find_file_by_fd (fd);
+    if (thread_file_temp && !inode_isdir(file_get_inode(thread_file_temp->file)))
+    {
+      /* Find the file, then read. */
+      acquire_lock_file ();
+      f->eax = file_read (thread_file_temp->file, buffer, size);
+      release_lock_file ();
+    } 
+    else
+    {
+      /* Otherwise, return error. */
+      exit_handler(f, -1);
+    }
+  }
+}
+
+void 
+syscall_write (struct intr_frame* f, int fd, const void* buffer, unsigned size)
+{
+  /* Check whole buffer. */
+  if (!is_vaild_buffer(buffer, size, false))
+  {
+    exit_handler (f, -1);
+  }
+  if (fd == STDIN_FILENO)
+  {
+    exit_handler (f, -1);
+  }
+
+  if (fd == STDOUT_FILENO)
+  {
+    /* Write to the console, return written number = size. */
+    putbuf (buffer, size);
+    f->eax = size;
+  }
+  else
+  {
+    /* Write to the file. */
+    struct thread_file *thread_file_temp = find_file_by_fd (fd);
+    if (thread_file_temp && !inode_isdir(file_get_inode(thread_file_temp->file)))   
+    {
+      /* Find the file, then write. */
+      acquire_lock_file ();
+      f->eax = file_write (thread_file_temp->file, buffer, size);
+      release_lock_file ();
+    } 
+    else
+    {
+      /* Otherwise, return error. */
+      exit_handler(f, -1);
+    }
+  }
+}
+
+void 
+syscall_seek (struct intr_frame *f, int fd, unsigned offset)
+{
+  struct thread_file *thread_file_temp = find_file_by_fd (fd);
+  if (thread_file_temp)
+  {
+    acquire_lock_file ();
+    file_seek (thread_file_temp->file, offset);
+    release_lock_file ();
+  }
+  else
+  {
+    exit_handler(f, -1);
+  }
+}
+
+void 
+syscall_tell (struct intr_frame *f, int fd)
+{
+  struct thread_file *thread_file_temp = find_file_by_fd (fd);
+  if (thread_file_temp && !inode_isdir(file_get_inode(thread_file_temp->file)))
+  {
+    acquire_lock_file ();
+    f->eax = file_tell (thread_file_temp->file);
+    release_lock_file ();
+  }
+  else
+  {
+    exit_handler(f, -1);
+  }
+}
+
+void 
+syscall_close (struct intr_frame *f, int fd)
+{
+  struct thread_file * opened_file = find_file_by_fd (fd);
+  if (opened_file)
+  {
+    acquire_lock_file ();
+#ifdef FILESYS
+    if (inode_isdir(file_get_inode(opened_file->file)))
+      dir_close(opened_file->opened_dir);
+#endif
+    file_close (opened_file->file);
+    release_lock_file ();
+    /* Remove the opened file from the list */
+    list_remove (&opened_file->file_elem);
+    /* Free opened files */
+    free (opened_file);
+  }
+  else
+  {
+    exit_handler(f, -1);
+  }
+}
+
+
+/* ----- Newly Add for Proj03 VM ----- */
+
+/* Handle memory-mapped file operations.
+   It maps a file into the address space of the process and returns a mapid.
+   If the mapping fails at any point, it returns MAP_FAILED. */
+void 
+syscall_mmap (struct intr_frame *f, int fd, const void *obj_vaddr)
+{
+  /* Check if the file descriptor is valid (not stdin or stdout). */
+  if (fd == 0 || fd == 1)
+  {
+    f->eax = MAP_FAILED;
+    return;
+  }
+
+  /* Ensure the virtual address is non-null and aligned to page boundary. */
+  if (obj_vaddr == NULL || ((uint32_t) obj_vaddr % (uint32_t)PGSIZE != 0))
+  {
+    f->eax = MAP_FAILED;
+    return;
+  }
+
+  /* Retrieve the current thread and its file handle for the given fd. */
+  struct thread *cur = thread_current ();
+  struct thread_file *fh = syscall_get_thread_file (fd);
+
+  /* Proceed if the file handle is valid. */
+  if (fh != NULL)
+  {
+    /* Generate a new mapid and allocate space for mmap_file structure. */
+    mapid_t mapid = cur->next_mapid++;
+    struct mmap_file *mf = malloc(sizeof(struct mmap_file));
+
+    /* Initialize mmap_file fields. */
+    mf->mapid = mapid;
+    mf->mmap_file = file_reopen(fh->file);  /* Reopen the file for mmap. */
+    mf->writable = true;
+    mf->is_segment = false;  /* It's a file-backed mapping, not a segment. */
+    mf->is_static_data = false;
+    mf->file_ofs = 0;
+
+    /* Calculate the number of pages required based on file size. */
+    off_t file_size = file_length(mf->mmap_file);
+    int num_page = file_size / PGSIZE;
+    int last_page_used = file_size % PGSIZE;
+
+    /* If there’s extra data for the last page, increase page count. */
+    if (last_page_used != 0)
+      num_page++;
+
+    /* Ensure the virtual address range is available for mapping. */
+    if (!mmap_check_mmap_vaddr(cur, obj_vaddr, num_page))
+    {
+      f->eax = MAP_FAILED;
+      return;
+    }
+
+    /* Set up mmap_file fields related to the virtual address range. */
+    mf->mmap_addr = obj_vaddr;
+    mf->num_page = num_page;
+    mf->num_page_with_segment = num_page;  /* For file-backed, the pages are the same. */
+    mf->last_page_size = last_page_used;
+
+    /* Add the mmap_file to the thread's mmap file list. */
+    list_push_back(&(cur->mmap_file_list), &(mf->elem));
+
+    /* Install pages into the page table. */
+    if (!mmap_install_page(cur, mf))
+    {
+      f->eax = MAP_FAILED;
+      return;
+    }
+
+    /* Return the mapid to the caller. */
+    f->eax = (uint32_t) mapid;
+  }
+  else
+  {
+    /* If the file handle is invalid, return MAP_FAILED. */
+    f->eax = MAP_FAILED;
+    return;
+  }
+}
+
+/* Unmap a memory-mapped file region, releasing associated resources.
+   It invalidates and removes the mapping for the given mapid, and frees the memory.
+   If any errors occur during the process, it returns MAP_FAILED. */
+void
+syscall_munmap(struct intr_frame *f, mapid_t mapid)
+{
+  struct thread *cur = thread_current();
+
+  /* If the current thread has no memory-mapped files, return MAP_FAILED. */
+  if (list_empty(&cur->mmap_file_list))
+  {
+    f->eax = MAP_FAILED;
+    return;
+  }
+
+  /* Retrieve the mmap_file handle for the given mapid. */
+  struct mmap_file *mf = syscall_get_mmap_handle(mapid);
+
+  /* If the handle is invalid (NULL), return MAP_FAILED. */
+  if (mf == NULL)
+  {
+    f->eax = MAP_FAILED;
+    return;
+  }
+
+  /* Unmap each page in the mmap_file's address range. */
+  for (int i = 0; i < mf->num_page; i++)
+  {
+    /* If unmapping fails for any page, clean up and return MAP_FAILED. */
+    if (!page_unmap(cur->page_table, mf->mmap_addr + i * PGSIZE))
+    {
+      delete_mmap_handle(mf);  /* Clean up mmap handle. */
+      f->eax = MAP_FAILED;
+      return;
+    }
+  }
+
+  /* Finally, remove the mmap handle. */
+  if (!delete_mmap_handle(mf))
+  {
+    f->eax = MAP_FAILED;
+    return;
+  }
+
+  /* Successfully unmapped the memory region. */
+}
+
+
+/* Check if the given virtual address range is available in the page table.
+   It ensures that the memory region represented by `vaddr` for `num_page` 
+   pages is valid and available. */
+bool
+mmap_check_mmap_vaddr(struct thread *cur, const void *vaddr, int num_page)
+{
+  /* Assume the memory range is available unless proven otherwise. */
+  bool res = true;  
+
+  /* Check if each page in the range is available. */
+  for (int i = 0; i < num_page; i++)
+  {
+    /* If the page is not available, set result to false and break out. */
+    if (!page_available_upage(cur->page_table, vaddr + i * PGSIZE))
+    {
+      res = false; 
+    }
+  }
+
+  return res;
+}
+
+
+/* Install the pages for the memory-mapped segment into the process's page table.
+   Iterate through the pages and installs each one using page_install_file. */
+bool
+mmap_install_page(struct thread *cur, struct mmap_file *mf)
+{
+  /* Initialize result as true, indicating success unless otherwise modified. */
+  bool res = true;  
+
+  /* Install pages for the mapped file region */
+  for (int i = 0; i < mf->num_page; i++)
+  {
+    /* Install the page for the current offset in the file */
+    if (!page_install_file(cur->page_table, mf, mf->mmap_addr + i * PGSIZE))
+    {
+      res = false;
+    }
+  }
+
+  /* Install pages for the segment region if it's a segment (not file-backed) */
+  if (mf->is_segment)
+  {
+    for (int i = mf->num_page; i < mf->num_page_with_segment; i++)
+    {
+      /* Install the page for the segment region */
+      if (!page_install_file(cur->page_table, mf, mf->mmap_addr + i * PGSIZE))
+      {
+        res = false;
+      }
+    }
+  }
+
+  return res;
+}
+
+/* Load a memory segment from a file into the user process's memory.
+   It maps the file segment into the user's virtual address space 
+   and installs the corresponding pages. */
+bool 
+mmap_load_segment(struct file *file, off_t ofs, uint8_t *upage, 
+                  uint32_t read_bytes, uint32_t zero_bytes, bool writable)
+{
+  /* Ensure the sum of read and zero bytes is page-aligned. */
+  ASSERT(!((read_bytes + zero_bytes) & PGMASK));  
+
+  struct thread* cur = thread_current();
+  
+  /* Assign a new map ID for the mapping. */
+  mapid_t mapid = cur->next_mapid++;
+
+  /* Allocate memory and set mapping infos for mmap_file structure. */
+  struct mmap_file *mf = malloc (sizeof (struct mmap_file));  
+  mf->mapid = mapid;
+  mf->mmap_file = file;
+  mf->writable = writable;
+  mf->is_static_data = writable;
+
+  /* Pages required for the read bytes. */
+  int num_page = read_bytes / PGSIZE;
+  /* Total pages including zeroed space. */
+  int total_num_page = ((read_bytes + zero_bytes) / PGSIZE);
+
+  /* Increment page count if the last page is partially used. */
+  int last_page_used = read_bytes & PGMASK;  
+  if (last_page_used != 0)
+  {
+    num_page++;  
+  }
+
+  /* Check if the virtual address range is valid for mapping. */
+  if (!mmap_check_mmap_vaddr(cur, upage, total_num_page))
+  {
+    return false;
+  }
+
+  /* Set up mmap_file structure */
+  mf->mmap_addr = upage;
+  mf->num_page = num_page;
+  mf->last_page_size = last_page_used;
+  mf->num_page_with_segment = total_num_page;
+  mf->is_segment = true;
+  mf->file_ofs = ofs;
+
+  /* Add the mapping entry to the thread's mmap list */
+  list_push_back(&(cur->mmap_file_list), &(mf->elem));
+  
+  /* Install pages for the segment into the process's page table */
+  if (!mmap_install_page(cur, mf))
+  {
+    return false;
+  }
+  
+  return true;
+}
+
+/* Reads data from the file or segment into the kernel page, 
+   depending on whether the memory map is backed by a file or segment. */
+void mmap_read_file(struct mmap_file* mf, void *upage, void *kpage)
+{
+  /* Case 1: The memory map is a segment (not file-backed). */
+  if (mf->is_segment)
+  {
+    /* Calculate the actual address of the last part of the segment. */
+    void* addr = mf->mmap_addr + mf->num_page * PGSIZE + mf->last_page_size;
+
+    /* Adjust the address if there is data on the last page. */
+    if(mf->last_page_size != 0)
+    {
+      addr -= PGSIZE;
+    }
+
+    /* If the address is larger than the user page address (upage). */
+    if (addr > upage)
+    {
+      /* If the remaining space is less than a full page, 
+         read the file and fill the remaining space with zeroes. */
+      if (addr - upage < PGSIZE)
+      {
+        file_read_at (mf->mmap_file, kpage, mf->last_page_size,
+                      upage - mf->mmap_addr + mf->file_ofs);
+        memset (kpage + mf->last_page_size, 0, PGSIZE - mf->last_page_size);
+      }
+      /* Otherwise, read a full page. */
+      else 
+      {
+        file_read_at (mf->mmap_file, kpage, PGSIZE, 
+                      upage - mf->mmap_addr + mf->file_ofs);
+      }
+    } 
+    /* If the address is not larger, 
+       just fill the remaining page with zeroes. */
+    else 
+    {
+      memset (kpage, 0, PGSIZE);
+    }
+  } 
+  /* Case 2: The memory map is file-backed. */
+  else
+  {
+    /* Check if the remaining space between the file size 
+       and the user page address is less than one page. */
+    if (mf->mmap_addr + file_length (mf->mmap_file) - upage < PGSIZE)
+    {
+      /* Read the file and fill the remaining space with zeroes. */
+      file_read_at (mf->mmap_file, kpage, mf->last_page_size,
+                    upage - mf->mmap_addr + mf->file_ofs);
+      memset (kpage + mf->last_page_size, 0, PGSIZE - mf->last_page_size);
+    } 
+    /* Otherwise, read a full page. */
+    else
+    {
+      file_read_at (mf->mmap_file, kpage, PGSIZE, 
+                    upage - mf->mmap_addr + mf->file_ofs);
+    }
+  }
+}
+
+/* Writes data from the kernel page to the file or segment, 
+   depending on whether the memory map is backed by a file or segment.*/
+void mmap_write_file(struct mmap_file* mf, void* upage, void *kpage)
+{
+  /* Check if the mapping is writable. */
+  if (mf->writable) 
+  {
+    /* Case 1: The memory map is a segment (not file-backed). */
+    if (mf->is_segment) 
+    {
+      /* Calculate the actual address of the last part of the segment. */
+      void *addr = mf->mmap_addr + mf->num_page * PGSIZE + mf->last_page_size;
+
+      if (addr > upage) 
+      {
+        /* If the address is greater than the user page address,
+           write the remaining part of the last page if it's less than a full page. */
+        if (addr - upage < PGSIZE) 
+        {
+          file_write_at (mf->mmap_file, kpage, mf->last_page_size,
+                         upage - mf->mmap_addr + mf->file_ofs);
+        }
+        /* Otherwise, write a full page. */
+        else
+        {
+          file_write_at (mf->mmap_file, kpage, PGSIZE,
+                         upage - mf->mmap_addr + mf->file_ofs);
+        }
+      }
+    } 
+    /* Case 2: The memory map is a file-backed mapping. */
+    else 
+    {
+      /* Check if the address difference between the file size 
+         and the user page address is less than one page. */
+      if (mf->mmap_addr + file_length (mf->mmap_file) - upage < PGSIZE)
+      {
+        /* Write the remaining part of the last page 
+           if it's smaller than a full page. */
+        file_write_at (mf->mmap_file, kpage, mf->last_page_size,
+                       upage - mf->mmap_addr + mf->file_ofs);
+      }
+      /* Otherwise, write a full page. */
+      else
+      {
+        file_write_at (mf->mmap_file, kpage, PGSIZE,
+                       upage - mf->mmap_addr + mf->file_ofs);
+      }
+    }
+  }
+}
+
+
+/* Some utility functions below. */
+
+/* Check if a user-provided pointer is valid.
+   Return true if all checks to its address pass. */
+bool 
+is_valid_uptr (void* uptr)
+{
+  /* Check the accessibility of next 4 bytes.
+     Ensures that the incoming arguments to system calls 
+     can be handled completely and safely. */
+  is_vaild_buffer(uptr, 4, false);
+}
+
+/* Check if a user-provided string is valid.
+   Return true if all chars in the string are vaild. */
+bool 
+is_valid_string (const char *ustr)
+{ 
+  if (!is_vaild_translate_vaddr (ustr, false))
+  {
+    return false;
+  }
+  int char_cnt = 0;
+  while (*ustr != '\0')
+  {
+    if (char_cnt == 4095)
+    {
+      printf ("ERROR! String is longer than 4096 Bytes.\n");
+      return false;
+    }
+    *ustr++;
+    char_cnt++;
+    if (((int)ustr & PGMASK) == 0)
+    {
+      if (!is_vaild_translate_vaddr (ustr, false))
+      {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+/* Check if a user-provided buffer (start at uaddr with size) is valid 
+   and accessible for reading (or even writing), ensuring that all 
+   addresses in the buffer are properly mapped to physical frame. */
+bool
+is_vaild_buffer (const char *uaddr, int size, bool is_write)
+{
+  if (!is_vaild_translate_vaddr (uaddr + size - 1, is_write))
+  {
+    return false;
+  }
+  
+  /* Count page_num by page size (4096 bytes). */
+  size >>= 12;  
+  
+  while (size--) 
+  {
+    if (!is_vaild_translate_vaddr (uaddr, is_write))
+    {
+      return false;
+    }
+    /* Test next virtual page. */
+    uaddr += (1 << 12);  
+  }
+  
+  return true;
+}
+
+/* Check and return if a given user virtual address has access to a physical frame 
+   (considering write permission), handling potential page faults if necessary. */
+bool
+is_vaild_translate_vaddr (const void *vaddr, bool is_write)
+{
+  /* Checks if the virtual address belongs to the user-space address range. */
+  if (vaddr == NULL || !is_user_vaddr(vaddr))
+  {
+    return false;
+  }
+#ifdef VM
+  struct page_table_elem* pte_found = page_find_with_lock (
+                thread_current ()->page_table, pg_round_down (vaddr));
+  if (pte_found == NULL)
+  {
+    /* Can not find a physical frame, then call page_fault. */
+    return page_fault_handler (vaddr, is_write, thread_current ()->esp);
+  }
+  else
+  {
+    /* Can find physical frame.
+       Return false only when is_write = 1 but writable = 0. */
+    return !(is_write && !(pte_found->writable));
+  }
+#else
+  /* Checks if the virtual address is mapped to a physical address. */
+  return (pagedir_get_page (thread_current()->pagedir, vaddr) != NULL);
+#endif
+}
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+static void
+syscall_chdir(struct intr_frame *f, const char *dir)
+{
+  if (!is_valid_string(dir) || strlen(dir) == 0)
+  {
+    f->eax = false;
+    return;
+  }
+  struct thread *cur = thread_current();
+  struct dir* target_dir;
+  char *pure_name = malloc(READDIR_MAX_LEN + 1);
+  bool is_dir;
+  ASSERT(dir != NULL)
+  ASSERT(pure_name != NULL)
+  if (is_rootpath(dir))
+  {
+    dir_close(cur->current_dir);
+    cur->current_dir = dir_open_root();
+    f->eax = true;
+    free(pure_name);
+  }
+  else
+  {
+    if (path_paser(dir, &target_dir, &pure_name, &is_dir))
+    {
+      ASSERT(target_dir != NULL)
+      ASSERT(pure_name != NULL)
+      struct dir *res = subdir_lookup(target_dir, pure_name);
+      if (res != NULL)
+      {
+        dir_close(cur->current_dir);
+        cur->current_dir = res;
+        f->eax = true;
+      }
+      else
+        f->eax = false;
+      dir_close(target_dir);
+    }
+    else
+      f->eax = false;
+    free(pure_name);
+  }
+}
+
+static void
+syscall_mkdir(struct intr_frame *f, const char *dir)
+{
+  if (!is_valid_string(dir) || strlen(dir) == 0)
+  {
+    f->eax = false;
+    return;
+  }
+  struct dir* target_dir;
+  char *pure_name = malloc(READDIR_MAX_LEN + 1);
+  bool is_dir;
+  ASSERT(dir != NULL)
+  ASSERT(pure_name != NULL)
+  if(!is_rootpath(dir) && path_paser(dir, &target_dir, &pure_name, &is_dir))
+  {
+    ASSERT(target_dir != NULL)
+    ASSERT(pure_name != NULL)
+    bool res = subdir_create(target_dir, pure_name);
+    dir_close(target_dir);
+    f->eax = res;
+  }
+  else
+    f->eax = false;
+  free(pure_name);
+}
+
+static void
+syscall_readdir(struct intr_frame *f, int fd, char *name)
+{
+  if (fd == 0 || fd == 1
+              || !is_vaild_buffer(name, READDIR_MAX_LEN + 1, false))
+  {
+    f->eax = false;
+    return;
+  }
+  struct thread_file *fh = syscall_get_thread_file(fd);
+  if (fh != NULL && is_dirfile(fh))
+  {
+    f->eax = dir_readdir(fh->opened_dir, name);
+  }
+  else
+    f->eax = false;
+}
+
+static void
+syscall_isdir(struct intr_frame *f, int fd)
+{
+  if (fd == 0 || fd == 1)
+  {
+    f->eax = false;
+    return;
+  }
+  struct thread_file *fh = syscall_get_thread_file(fd);
+  f->eax = fh != NULL && is_dirfile(fh);
+}
+
+static void
+syscall_inumber(struct intr_frame *f, int fd)
+{
+  if (fd == 0 || fd == 1)
+  {
+    exit_handler(f, -1);
+  }
+  struct thread_file *fh = syscall_get_thread_file(fd);
+  if (fh != NULL)
+  {
+    f->eax = inode_get_inumber(file_get_inode(fh->file));
+    return;
+  }
+  else
+    f->eax = -1;
+}
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+/* Find file through the file descriptor. */
+struct thread_file * 
+find_file_by_fd (int aim_fd)
+{
+  struct list_elem *e;
+  struct thread_file * thread_file_temp = NULL;
+  struct list *files = &thread_current ()->files_list;
+  for (e = list_begin (files); e != list_end (files); e = list_next (e))
+  {
+    thread_file_temp = list_entry (e, struct thread_file, file_elem);
+    if (aim_fd == thread_file_temp->fd)
+      if (thread_file_temp->owned_thread != thread_current())
+        return NULL;
+      else
+        return thread_file_temp;
+  }
+  return false;
+}
+
+/* ----- Newly Add for Proj03 VM ----- */
\ No newline at end of file
diff --git a/src/userprog/syscall.h b/src/userprog/syscall.h
new file mode 100644
index 0000000..479311c
--- /dev/null
+++ b/src/userprog/syscall.h
@@ -0,0 +1,39 @@
+#ifndef USERPROG_SYSCALL_H
+#define USERPROG_SYSCALL_H
+
+#include <stdbool.h>
+#include "lib/user/syscall.h"
+#include "filesys/off_t.h"
+#include "threads/interrupt.h"
+#include "filesys/file.h"
+
+#include <threads/thread.h>
+#include <lib/string.h>
+#include "lib/stddef.h"
+
+void syscall_init (void);
+
+/* ----- Newly Add for Proj02 UserProg ----- */
+bool is_valid_uptr (void *uptr);
+bool is_valid_string (const char *ustr);
+bool is_vaild_buffer(const char* ustr, int size, bool write);
+void exit_handler (struct intr_frame *f, int status);
+
+struct thread_file *find_file_by_fd(int aim_fd);
+/* ----- Newly Add for Proj02 UserProg ----- */
+
+/* ----- Newly Add for Proj03 VM ----- */
+bool is_vaild_translate_vaddr(const void *vaddr, bool write);
+bool mmap_check_mmap_vaddr(struct thread *cur, const void *vaddr, int num_page);
+bool mmap_install_page(struct thread *cur, struct mmap_file *mh);
+void mmap_read_file(struct mmap_file* mh, void *upage, void *kpage);
+void mmap_write_file(struct mmap_file* mh, void *upage, void *kpage);
+bool mmap_load_segment(struct file *file, off_t ofs, uint8_t *upage, uint32_t read_bytes, uint32_t zero_bytes, bool writable);
+/* ----- Newly Add for Proj03 VM ----- */
+
+/* ----- Newly Add for Proj04 FileSys ----- */
+void syscall_file_close(struct file* file);
+struct file* syscall_file_open(const char* name);
+/* ----- Newly Add for Proj04 FileSys ----- */
+
+#endif /* userprog/syscall.h */
diff --git a/src/userprog/tss.c b/src/userprog/tss.c
new file mode 100644
index 0000000..f8ed9a9
--- /dev/null
+++ b/src/userprog/tss.c
@@ -0,0 +1,106 @@
+#include "userprog/tss.h"
+#include <debug.h>
+#include <stddef.h>
+#include "userprog/gdt.h"
+#include "threads/thread.h"
+#include "threads/palloc.h"
+#include "threads/vaddr.h"
+
+/* The Task-State Segment (TSS).
+
+   Instances of the TSS, an x86-specific structure, are used to
+   define "tasks", a form of support for multitasking built right
+   into the processor.  However, for various reasons including
+   portability, speed, and flexibility, most x86 OSes almost
+   completely ignore the TSS.  We are no exception.
+
+   Unfortunately, there is one thing that can only be done using
+   a TSS: stack switching for interrupts that occur in user mode.
+   When an interrupt occurs in user mode (ring 3), the processor
+   consults the ss0 and esp0 members of the current TSS to
+   determine the stack to use for handling the interrupt.  Thus,
+   we must create a TSS and initialize at least these fields, and
+   this is precisely what this file does.
+
+   When an interrupt is handled by an interrupt or trap gate
+   (which applies to all interrupts we handle), an x86 processor
+   works like this:
+
+     - If the code interrupted by the interrupt is in the same
+       ring as the interrupt handler, then no stack switch takes
+       place.  This is the case for interrupts that happen when
+       we're running in the kernel.  The contents of the TSS are
+       irrelevant for this case.
+
+     - If the interrupted code is in a different ring from the
+       handler, then the processor switches to the stack
+       specified in the TSS for the new ring.  This is the case
+       for interrupts that happen when we're in user space.  It's
+       important that we switch to a stack that's not already in
+       use, to avoid corruption.  Because we're running in user
+       space, we know that the current process's kernel stack is
+       not in use, so we can always use that.  Thus, when the
+       scheduler switches threads, it also changes the TSS's
+       stack pointer to point to the new thread's kernel stack.
+       (The call is in thread_schedule_tail() in thread.c.)
+
+   See [IA32-v3a] 6.2.1 "Task-State Segment (TSS)" for a
+   description of the TSS.  See [IA32-v3a] 5.12.1 "Exception- or
+   Interrupt-Handler Procedures" for a description of when and
+   how stack switching occurs during an interrupt. */
+struct tss
+  {
+    uint16_t back_link, :16;
+    void *esp0;                         /* Ring 0 stack virtual address. */
+    uint16_t ss0, :16;                  /* Ring 0 stack segment selector. */
+    void *esp1;
+    uint16_t ss1, :16;
+    void *esp2;
+    uint16_t ss2, :16;
+    uint32_t cr3;
+    void (*eip) (void);
+    uint32_t eflags;
+    uint32_t eax, ecx, edx, ebx;
+    uint32_t esp, ebp, esi, edi;
+    uint16_t es, :16;
+    uint16_t cs, :16;
+    uint16_t ss, :16;
+    uint16_t ds, :16;
+    uint16_t fs, :16;
+    uint16_t gs, :16;
+    uint16_t ldt, :16;
+    uint16_t trace, bitmap;
+  };
+
+/* Kernel TSS. */
+static struct tss *tss;
+
+/* Initializes the kernel TSS. */
+void
+tss_init (void) 
+{
+  /* Our TSS is never used in a call gate or task gate, so only a
+     few fields of it are ever referenced, and those are the only
+     ones we initialize. */
+  tss = palloc_get_page (PAL_ASSERT | PAL_ZERO);
+  tss->ss0 = SEL_KDSEG;
+  tss->bitmap = 0xdfff;
+  tss_update ();
+}
+
+/* Returns the kernel TSS. */
+struct tss *
+tss_get (void) 
+{
+  ASSERT (tss != NULL);
+  return tss;
+}
+
+/* Sets the ring 0 stack pointer in the TSS to point to the end
+   of the thread stack. */
+void
+tss_update (void) 
+{
+  ASSERT (tss != NULL);
+  tss->esp0 = (uint8_t *) thread_current () + PGSIZE;
+}
diff --git a/src/userprog/tss.h b/src/userprog/tss.h
new file mode 100644
index 0000000..467bd19
--- /dev/null
+++ b/src/userprog/tss.h
@@ -0,0 +1,11 @@
+#ifndef USERPROG_TSS_H
+#define USERPROG_TSS_H
+
+#include <stdint.h>
+
+struct tss;
+void tss_init (void);
+struct tss *tss_get (void);
+void tss_update (void);
+
+#endif /* userprog/tss.h */
diff --git a/src/utils/.gitignore b/src/utils/.gitignore
new file mode 100644
index 0000000..b96f278
--- /dev/null
+++ b/src/utils/.gitignore
@@ -0,0 +1,3 @@
+setitimer-helper
+squish-pty
+squish-unix
diff --git a/src/utils/Makefile b/src/utils/Makefile
new file mode 100644
index 0000000..e38eb1f
--- /dev/null
+++ b/src/utils/Makefile
@@ -0,0 +1,11 @@
+all: setitimer-helper squish-pty squish-unix
+
+CC = gcc
+CFLAGS = -Wall -W
+LOADLIBES = -lm
+setitimer-helper: setitimer-helper.o
+squish-pty: squish-pty.o
+squish-unix: squish-unix.o
+
+clean: 
+	rm -f *.o setitimer-helper squish-pty squish-unix
diff --git a/src/utils/Pintos.pm b/src/utils/Pintos.pm
new file mode 100644
index 0000000..70df40d
--- /dev/null
+++ b/src/utils/Pintos.pm
@@ -0,0 +1,491 @@
+# Pintos helper subroutines.
+
+# Number of bytes available for the loader at the beginning of the MBR.
+# Kernel command-line arguments follow the loader.
+our $LOADER_SIZE = 314;
+
+# Partition types.
+my (%role2type) = (KERNEL => 0x20,
+		   FILESYS => 0x21,
+		   SCRATCH => 0x22,
+		   SWAP => 0x23);
+my (%type2role) = reverse %role2type;
+
+# Order of roles within a given disk.
+our (@role_order) = qw (KERNEL FILESYS SCRATCH SWAP);
+
+# Partitions.
+#
+# Valid keys are KERNEL, FILESYS, SCRATCH, SWAP.  Only those
+# partitions which are in use are included.
+#
+# Each value is a reference to a hash.  If the partition's contents
+# are to be obtained from a file (that will be copied into a new
+# virtual disk), then the hash contains:
+#
+# FILE => name of file from which the partition's contents are copied
+#         (perhaps "/dev/zero"),
+# OFFSET => offset in bytes in FILE,
+# BYTES => size in bytes of contents from FILE,
+#
+# If the partition is taken from a virtual disk directly, then it
+# contains the following.  The same keys are also filled in once a
+# file-based partition has been copied into a new virtual disk:
+#
+# DISK => name of virtual disk file,
+# START => sector offset of start of partition within DISK,
+# SECTORS => number of sectors of partition within DISK, which is usually
+#            greater than round_up (BYTES, 512) due to padding.
+our (%parts);
+
+# set_part($opt, $arg)
+#
+# For use as a helper function for Getopt::Long::GetOptions to set
+# disk sources.
+sub set_part {
+    my ($opt, $arg) = @_;
+    my ($role, $source) = $opt =~ /^([a-z]+)(?:-([a-z]+))?/ or die;
+
+    $role = uc $role;
+    $source = 'FILE' if $source eq '';
+
+    die "can't have two sources for \L$role\E partition"
+      if exists $parts{$role};
+
+    do_set_part ($role, $source, $arg);
+}
+
+# do_set_part($role, $source, $arg)
+#
+# Sets partition $role as coming from $source (one of 'file', 'from',
+# or 'size').  $arg is a file name for 'file' or 'from', a size in
+# megabytes for 'size'.
+sub do_set_part {
+    my ($role, $source, $arg) = @_;
+
+    my ($p) = $parts{$role} = {};
+    if ($source eq 'file') {
+	if (read_mbr ($arg)) {
+	    print STDERR "warning: $arg looks like a partitioned disk ";
+	    print STDERR "(did you want --$role-from=$arg or --disk=$arg?)\n"
+	}
+
+	$p->{FILE} = $arg;
+	$p->{OFFSET} = 0;
+	$p->{BYTES} = -s $arg;
+    } elsif ($source eq 'from') {
+	my (%pt) = read_partition_table ($arg);
+	my ($sp) = $pt{$role};
+	die "$arg: does not contain \L$role\E partition\n" if !defined $sp;
+
+	$p->{FILE} = $arg;
+	$p->{OFFSET} = $sp->{START} * 512;
+	$p->{BYTES} = $sp->{SECTORS} * 512;
+    } elsif ($source eq 'size') {
+	$arg =~ /^\d+(\.\d+)?|\.\d+$/ or die "$arg: not a valid size in MB\n";
+
+	$p->{FILE} = "/dev/zero";
+	$p->{OFFSET} = 0;
+	$p->{BYTES} = ceil ($arg * 1024 * 1024);
+    } else {
+	die;
+    }
+}
+
+# set_geometry('HEADS,SPT')
+# set_geometry('zip')
+#
+# For use as a helper function for Getopt::Long::GetOptions to set
+# disk geometry.
+sub set_geometry {
+    local ($_) = $_[1];
+    if ($_ eq 'zip') {
+	@geometry{'H', 'S'} = (64, 32);
+    } else {
+	@geometry{'H', 'S'} = /^(\d+)[,\s]+(\d+)$/
+	  or die "bad syntax for geometry\n";
+	$geometry{H} <= 255 or die "heads limited to 255\n";
+	$geometry{S} <= 63 or die "sectors per track limited to 63\n";
+    }
+}
+
+# set_align('bochs|full|none')
+#
+# For use as a helper function for Getopt::Long::GetOptions to set
+# partition alignment.
+sub set_align {
+    $align = $_[1];
+    die "unknown alignment type \"$align\"\n"
+      if $align ne 'bochs' && $align ne 'full' && $align ne 'none';
+}
+
+# assemble_disk(%args)
+#
+# Creates a virtual disk $args{DISK} containing the partitions
+# described by @args{KERNEL, FILESYS, SCRATCH, SWAP}.
+#
+# Required arguments:
+#   DISK => output disk file name
+#   HANDLE => output file handle (will be closed)
+#
+# Normally at least one of the following is included:
+#   KERNEL, FILESYS, SCRATCH, SWAP => {input:
+#				       FILE => file to read,
+#                                      OFFSET => byte offset in file,
+#                                      BYTES => byte count from file,
+#
+#                                      output:
+#				       DISK => output disk file name,
+#                                      START => sector offset in DISK,
+#                                      SECTORS => sector count in DISK},
+#
+# Optional arguments:
+#   ALIGN => 'bochs' (default), 'full', or 'none'
+#   GEOMETRY => {H => heads, S => sectors per track} (default 16, 63)
+#   FORMAT => 'partitioned' (default) or 'raw'
+#   LOADER => $LOADER_SIZE-byte string containing the loader binary
+#   ARGS => ['arg 1', 'arg 2', ...]
+sub assemble_disk {
+    my (%args) = @_;
+
+    my (%geometry) = $args{GEOMETRY} || (H => 16, S => 63);
+
+    my ($align);	# Align partition start, end to cylinder boundary?
+    my ($pad);		# Pad end of disk out to cylinder boundary?
+    if (!defined ($args{ALIGN}) || $args{ALIGN} eq 'bochs') {
+	$align = 0;
+	$pad = 1;
+    } elsif ($args{ALIGN} eq 'full') {
+	$align = 1;
+	$pad = 0;
+    } elsif ($args{ALIGN} eq 'none') {
+	$align = $pad = 0;
+    } else {
+	die;
+    }
+
+    my ($format) = $args{FORMAT} || 'partitioned';
+    die if $format ne 'partitioned' && $format ne 'raw';
+
+    # Check that we have apartitions to copy in.
+    my $part_cnt = grep (defined ($args{$_}), keys %role2type);
+    die "must have exactly one partition for raw output\n"
+      if $format eq 'raw' && $part_cnt != 1;
+
+    # Calculate the disk size.
+    my ($total_sectors) = 0;
+    if ($format eq 'partitioned') {
+	$total_sectors += $align ? $geometry{S} : 1;
+    }
+    for my $role (@role_order) {
+	my ($p) = $args{$role};
+	next if !defined $p;
+
+	die if $p->{DISK};
+
+	my ($bytes) = $p->{BYTES};
+	my ($start) = $total_sectors;
+	my ($end) = $start + div_round_up ($bytes, 512);
+	$end = round_up ($end, cyl_sectors (%geometry)) if $align;
+
+	$p->{DISK} = $args{DISK};
+	$p->{START} = $start;
+	$p->{SECTORS} = $end - $start;
+	$total_sectors = $end;
+    }
+
+    # Write the disk.
+    my ($disk_fn) = $args{DISK};
+    my ($disk) = $args{HANDLE};
+    if ($format eq 'partitioned') {
+	# Pack loader into MBR.
+	my ($loader) = $args{LOADER} || "\xcd\x18";
+	my ($mbr) = pack ("a$LOADER_SIZE", $loader);
+
+	$mbr .= make_kernel_command_line (@{$args{ARGS}});
+
+	# Pack partition table into MBR.
+	$mbr .= make_partition_table (\%geometry, \%args);
+
+	# Add signature to MBR.
+	$mbr .= pack ("v", 0xaa55);
+
+	die if length ($mbr) != 512;
+	write_fully ($disk, $disk_fn, $mbr);
+	write_zeros ($disk, $disk_fn, 512 * ($geometry{S} - 1)) if $align;
+    }
+    for my $role (@role_order) {
+	my ($p) = $args{$role};
+	next if !defined $p;
+
+	my ($source);
+	my ($fn) = $p->{FILE};
+	open ($source, '<', $fn) or die "$fn: open: $!\n";
+	if ($p->{OFFSET}) {
+	    sysseek ($source, $p->{OFFSET}, 0) == $p->{OFFSET}
+	      or die "$fn: seek: $!\n";
+	}
+	copy_file ($source, $fn, $disk, $disk_fn, $p->{BYTES});
+	close ($source) or die "$fn: close: $!\n";
+
+	write_zeros ($disk, $disk_fn, $p->{SECTORS} * 512 - $p->{BYTES});
+    }
+    if ($pad) {
+	my ($pad_sectors) = round_up ($total_sectors, cyl_sectors (%geometry));
+	write_zeros ($disk, $disk_fn, ($pad_sectors - $total_sectors) * 512);
+    }
+    close ($disk) or die "$disk: close: $!\n";
+}
+
+# make_partition_table({H => heads, S => sectors}, {KERNEL => ..., ...})
+#
+# Creates and returns a partition table for the given partitions and
+# disk geometry.
+sub make_partition_table {
+    my ($geometry, $partitions) = @_;
+    my ($table) = '';
+    for my $role (@role_order) {
+	defined (my $p = $partitions->{$role}) or next;
+
+	my $end = $p->{START} + $p->{SECTORS} - 1;
+	my $bootable = $role eq 'KERNEL';
+
+	$table .= pack ("C", $bootable ? 0x80 : 0);   # Bootable?
+	$table .= pack_chs ($p->{START}, $geometry);  # CHS of partition start
+	$table .= pack ("C", $role2type{$role});      # Partition type
+	$table .= pack_chs($end, $geometry);          # CHS of partition end
+	$table .= pack ("V", $p->{START});            # LBA of partition start
+	$table .= pack ("V", $p->{SECTORS});          # Length in sectors
+	die if length ($table) % 16;
+    }
+    return pack ("a64", $table);
+}
+
+# make_kernel_command_line(@args)
+#
+# Returns the raw bytes to write to an MBR at offset $LOADER_SIZE to
+# set a Pintos kernel command line.
+sub make_kernel_command_line {
+    my (@args) = @_;
+    my ($args) = join ('', map ("$_\0", @args));
+    die "command line exceeds 128 bytes" if length ($args) > 128;
+    return pack ("V a128", scalar (@args), $args);
+}
+
+# copy_file($from_handle, $from_file_name, $to_handle, $to_file_name, $size)
+#
+# Copies $size bytes from $from_handle to $to_handle.
+# $from_file_name and $to_file_name are used in error messages.
+sub copy_file {
+    my ($from_handle, $from_file_name, $to_handle, $to_file_name, $size) = @_;
+
+    while ($size > 0) {
+	my ($chunk_size) = 4096;
+	$chunk_size = $size if $chunk_size > $size;
+	$size -= $chunk_size;
+
+	my ($data) = read_fully ($from_handle, $from_file_name, $chunk_size);
+	write_fully ($to_handle, $to_file_name, $data);
+    }
+}
+
+# read_fully($handle, $file_name, $bytes)
+#
+# Reads exactly $bytes bytes from $handle and returns the data read.
+# $file_name is used in error messages.
+sub read_fully {
+    my ($handle, $file_name, $bytes) = @_;
+    my ($data);
+    my ($read_bytes) = sysread ($handle, $data, $bytes);
+    die "$file_name: read: $!\n" if !defined $read_bytes;
+    die "$file_name: unexpected end of file\n" if $read_bytes != $bytes;
+    return $data;
+}
+
+# write_fully($handle, $file_name, $data)
+#
+# Write $data to $handle.
+# $file_name is used in error messages.
+sub write_fully {
+    my ($handle, $file_name, $data) = @_;
+    my ($written_bytes) = syswrite ($handle, $data);
+    die "$file_name: write: $!\n" if !defined $written_bytes;
+    die "$file_name: short write\n" if $written_bytes != length $data;
+}
+
+sub write_zeros {
+    my ($handle, $file_name, $size) = @_;
+
+    while ($size > 0) {
+	my ($chunk_size) = 4096;
+	$chunk_size = $size if $chunk_size > $size;
+	$size -= $chunk_size;
+
+	write_fully ($handle, $file_name, "\0" x $chunk_size);
+    }
+}
+
+# div_round_up($x,$y)
+#
+# Returns $x / $y, rounded up to the nearest integer.
+# $y must be an integer.
+sub div_round_up {
+    my ($x, $y) = @_;
+    return int ((ceil ($x) + $y - 1) / $y);
+}
+
+# round_up($x, $y)
+#
+# Returns $x rounded up to the nearest multiple of $y.
+# $y must be an integer.
+sub round_up {
+    my ($x, $y) = @_;
+    return div_round_up ($x, $y) * $y;
+}
+
+# cyl_sectors(H => heads, S => sectors)
+#
+# Returns the number of sectors in a cylinder of a disk with the given
+# geometry.
+sub cyl_sectors {
+    my (%geometry) = @_;
+    return $geometry{H} * $geometry{S};
+}
+
+# read_loader($file_name)
+#
+# Reads and returns the first $LOADER_SIZE bytes in $file_name.
+# If $file_name is undefined, tries to find the default loader.
+# Makes sure that the loader is a reasonable size.
+sub read_loader {
+    my ($name) = @_;
+    $name = find_file ("loader.bin") if !defined $name;
+    die "Cannot find loader\n" if !defined $name;
+
+    my ($handle);
+    open ($handle, '<', $name) or die "$name: open: $!\n";
+    -s $handle == $LOADER_SIZE || -s $handle == 512
+      or die "$name: must be exactly $LOADER_SIZE or 512 bytes long\n";
+    $loader = read_fully ($handle, $name, $LOADER_SIZE);
+    close ($handle) or die "$name: close: $!\n";
+    return $loader;
+}
+
+# pack_chs($lba, {H => heads, S => sectors})
+#
+# Converts logical sector $lba to a 3-byte packed geometrical sector
+# in the format used in PC partition tables (see [Partitions]) and
+# returns the geometrical sector as a 3-byte string.
+sub pack_chs {
+    my ($lba, $geometry) = @_;
+    my ($cyl, $head, $sect) = lba_to_chs ($lba, $geometry);
+    return pack ("CCC", $head, $sect | (($cyl >> 2) & 0xc0), $cyl & 0xff);
+}
+
+# lba_to_chs($lba, {H => heads, S => sectors})
+#
+# Returns the geometrical sector corresponding to logical sector $lba
+# given the specified geometry.
+sub lba_to_chs {
+    my ($lba, $geometry) = @_;
+    my ($hpc) = $geometry->{H};
+    my ($spt) = $geometry->{S};
+
+    # Source:
+    # http://en.wikipedia.org/wiki/CHS_conversion
+    use integer;
+    my $cyl = $lba / ($hpc * $spt);
+    my $temp = $lba % ($hpc * $spt);
+    my $head = $temp / $spt;
+    my $sect = $temp % $spt + 1;
+
+    # Source:
+    # http://www.cgsecurity.org/wiki/Intel_Partition_Table
+    if ($cyl <= 1023) {
+        return ($cyl, $head, $sect);
+    } else {
+        return (1023, 254, 63);	## or should this be (1023, $hpc, $spt)?
+    }
+}
+
+# read_mbr($file)
+#
+# Tries to read an MBR from $file.  Returns the 512-byte MBR if
+# successful, otherwise numeric 0.
+sub read_mbr {
+    my ($file) = @_;
+    my ($retval) = 0;
+    open (FILE, '<', $file) or die "$file: open: $!\n";
+    if (-s FILE == 0) {
+	die "$file: file has zero size\n";
+    } elsif (-s FILE >= 512) {
+	my ($mbr);
+	sysread (FILE, $mbr, 512) == 512 or die "$file: read: $!\n";
+	$retval = $mbr if unpack ("v", substr ($mbr, 510)) == 0xaa55;
+    }
+    close (FILE);
+    return $retval;
+}
+
+# interpret_partition_table($mbr, $disk)
+#
+# Parses the partition-table in the specified 512-byte $mbr and
+# returns the partitions.  $disk is used for error messages.
+sub interpret_partition_table {
+    my ($mbr, $disk) = @_;
+    my (%parts);
+    for my $i (0...3) {
+	my ($bootable, $valid, $type, $lba_start, $lba_length)
+	  = unpack ("C X V C x3 V V", substr ($mbr, 446 + 16 * $i, 16));
+	next if !$valid;
+
+	(print STDERR "warning: invalid partition entry $i in $disk\n"),
+	  next if $bootable != 0 && $bootable != 0x80;
+
+	my ($role) = $type2role{$type};
+	(printf STDERR "warning: non-Pintos partition type 0x%02x in %s\n",
+	 $type, $disk),
+	  next if !defined $role;
+
+	(print STDERR "warning: duplicate \L$role\E partition in $disk\n"),
+	  next if exists $parts{$role};
+
+	$parts{$role} = {START => $lba_start,
+			 SECTORS => $lba_length};
+    }
+    return %parts;
+}
+
+# find_file($base_name)
+#
+# Looks for a file named $base_name in a couple of likely spots.  If
+# found, returns the name; otherwise, returns undef.
+sub find_file {
+    my ($base_name) = @_;
+    -e && return $_ foreach $base_name, "build/$base_name";
+    return undef;
+}
+
+# read_partition_table($file)
+#
+# Reads a partition table from $file and returns the parsed
+# partitions.  Dies if partitions can't be read.
+sub read_partition_table {
+    my ($file) = @_;
+    my ($mbr) = read_mbr ($file);
+    die "$file: not a partitioned disk\n" if !$mbr;
+    return interpret_partition_table ($mbr, $file);
+}
+
+# max(@args)
+#
+# Returns the numerically largest value in @args.
+sub max {
+    my ($max) = $_[0];
+    foreach (@_[1..$#_]) {
+	$max = $_ if $_ > $max;
+    }
+    return $max;
+}
+
+1;
diff --git a/src/utils/backtrace b/src/utils/backtrace
new file mode 100644
index 0000000..95e422f
--- /dev/null
+++ b/src/utils/backtrace
@@ -0,0 +1,106 @@
+#! /usr/bin/perl -w
+
+use strict;
+
+# Check command line.
+if (grep ($_ eq '-h' || $_ eq '--help', @ARGV)) {
+    print <<'EOF';
+backtrace, for converting raw addresses into symbolic backtraces
+usage: backtrace [BINARY]... ADDRESS...
+where BINARY is the binary file or files from which to obtain symbols
+ and ADDRESS is a raw address to convert to a symbol name.
+
+If no BINARY is unspecified, the default is the first of kernel.o or
+build/kernel.o that exists.  If multiple binaries are specified, each
+symbol printed is from the first binary that contains a match.
+
+The ADDRESS list should be taken from the "Call stack:" printed by the
+kernel.  Read "Backtraces" in the "Debugging Tools" chapter of the
+Pintos documentation for more information.
+EOF
+    exit 0;
+}
+die "backtrace: at least one argument required (use --help for help)\n"
+    if @ARGV == 0;
+
+# Drop garbage inserted by kernel.
+@ARGV = grep (!/^(call|stack:?|[-+])$/i, @ARGV);
+s/\.$// foreach @ARGV;
+
+# Find binaries.
+my (@binaries);
+while ($ARGV[0] !~ /^0x/) {
+    my ($bin) = shift @ARGV;
+    die "backtrace: $bin: not found (use --help for help)\n" if ! -e $bin;
+    push (@binaries, $bin);
+}
+if (!@binaries) {
+    my ($bin);
+    if (-e 'kernel.o') {
+	$bin = 'kernel.o';
+    } elsif (-e 'build/kernel.o') {
+	$bin = 'build/kernel.o';
+    } else {
+	die "backtrace: no binary specified and neither \"kernel.o\" nor \"build/kernel.o\" exists (use --help for help)\n";
+    }
+    push (@binaries, $bin);
+}
+
+# Find addr2line.
+my ($a2l) = search_path ("i386-elf-addr2line") || search_path ("addr2line");
+if (!$a2l) {
+    die "backtrace: neither `i386-elf-addr2line' nor `addr2line' in PATH\n";
+}
+sub search_path {
+    my ($target) = @_;
+    for my $dir (split (':', $ENV{PATH})) {
+	my ($file) = "$dir/$target";
+	return $file if -e $file;
+    }
+    return undef;
+}
+
+# Figure out backtrace.
+my (@locs) = map ({ADDR => $_}, @ARGV);
+for my $bin (@binaries) {
+    open (A2L, "$a2l -fe $bin " . join (' ', map ($_->{ADDR}, @locs)) . "|");
+    for (my ($i) = 0; <A2L>; $i++) {
+	my ($function, $line);
+	chomp ($function = $_);
+	chomp ($line = <A2L>);
+	next if defined $locs[$i]{BINARY};
+
+	if ($function ne '??' || $line ne '??:0') {
+	    $locs[$i]{FUNCTION} = $function;
+	    $locs[$i]{LINE} = $line;
+	    $locs[$i]{BINARY} = $bin;
+	}
+    }
+    close (A2L);
+}
+
+# Print backtrace.
+my ($cur_binary);
+for my $loc (@locs) {
+    if (defined ($loc->{BINARY})
+	&& @binaries > 1
+	&& (!defined ($cur_binary) || $loc->{BINARY} ne $cur_binary)) {
+	$cur_binary = $loc->{BINARY};
+	print "In $cur_binary:\n";
+    }
+
+    my ($addr) = $loc->{ADDR};
+    $addr = sprintf ("0x%08x", hex ($addr)) if $addr =~ /^0x[0-9a-f]+$/i;
+
+    print $addr, ": ";
+    if (defined ($loc->{BINARY})) {
+	my ($function) = $loc->{FUNCTION};
+	my ($line) = $loc->{LINE};
+	$line =~ s/^(\.\.\/)*//;
+	$line = "..." . substr ($line, -25) if length ($line) > 28;
+	print "$function ($line)";
+    } else {
+	print "(unknown)";
+    }
+    print "\n";
+}
diff --git a/src/utils/pintos b/src/utils/pintos
new file mode 100644
index 0000000..58635a9
--- /dev/null
+++ b/src/utils/pintos
@@ -0,0 +1,962 @@
+#! /usr/bin/perl -w
+
+use strict;
+use POSIX;
+use Fcntl;
+use File::Temp 'tempfile';
+use Getopt::Long qw(:config bundling);
+use Fcntl qw(SEEK_SET SEEK_CUR);
+
+# Read Pintos.pm from the same directory as this program.
+BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; }
+
+# Command-line options.
+our ($start_time) = time ();
+our ($sim);			# Simulator: bochs, qemu, or player.
+our ($debug) = "none";		# Debugger: none, monitor, or gdb.
+our ($mem) = 4;			# Physical RAM in MB.
+our ($serial) = 1;		# Use serial port for input and output?
+our ($vga);			# VGA output: window, terminal, or none.
+our ($jitter);			# Seed for random timer interrupts, if set.
+our ($realtime);		# Synchronize timer interrupts with real time?
+our ($timeout);			# Maximum runtime in seconds, if set.
+our ($kill_on_failure);		# Abort quickly on test failure?
+our (@puts);			# Files to copy into the VM.
+our (@gets);			# Files to copy out of the VM.
+our ($as_ref);			# Reference to last addition to @gets or @puts.
+our (@kernel_args);		# Arguments to pass to kernel.
+our (%parts);			# Partitions.
+our ($make_disk);		# Name of disk to create.
+our ($tmp_disk) = 1;		# Delete $make_disk after run?
+our (@disks);			# Extra disk images to pass to simulator.
+our ($loader_fn);		# Bootstrap loader.
+our (%geometry);		# IDE disk geometry.
+our ($align);			# Partition alignment.
+our ($gdb_port) = $ENV{"GDB_PORT"} || "1234"; # Port to listen on for GDB
+
+parse_command_line ();
+prepare_scratch_disk ();
+find_disks ();
+run_vm ();
+finish_scratch_disk ();
+
+exit 0;
+
+# Parses the command line.
+sub parse_command_line {
+    usage (0) if @ARGV == 0 || (@ARGV == 1 && $ARGV[0] eq '--help');
+
+    @kernel_args = @ARGV;
+    if (grep ($_ eq '--', @kernel_args)) {
+	@ARGV = ();
+	while ((my $arg = shift (@kernel_args)) ne '--') {
+	    push (@ARGV, $arg);
+	}
+	GetOptions ("sim=s" => sub { set_sim ($_[1]) },
+		    "bochs" => sub { set_sim ("bochs") },
+		    "qemu" => sub { set_sim ("qemu") },
+		    "player" => sub { set_sim ("player") },
+
+		    "debug=s" => sub { set_debug ($_[1]) },
+		    "no-debug" => sub { set_debug ("none") },
+		    "monitor" => sub { set_debug ("monitor") },
+		    "gdb" => sub { set_debug ("gdb") },
+
+		    "m|memory=i" => \$mem,
+		    "j|jitter=i" => sub { set_jitter ($_[1]) },
+		    "r|realtime" => sub { set_realtime () },
+
+		    "T|timeout=i" => \$timeout,
+		    "k|kill-on-failure" => \$kill_on_failure,
+
+		    "v|no-vga" => sub { set_vga ('none'); },
+		    "s|no-serial" => sub { $serial = 0; },
+		    "t|terminal" => sub { set_vga ('terminal'); },
+
+		    "p|put-file=s" => sub { add_file (\@puts, $_[1]); },
+		    "g|get-file=s" => sub { add_file (\@gets, $_[1]); },
+		    "a|as=s" => sub { set_as ($_[1]); },
+
+		    "h|help" => sub { usage (0); },
+
+		    "kernel=s" => \&set_part,
+		    "filesys=s" => \&set_part,
+		    "swap=s" => \&set_part,
+
+		    "filesys-size=s" => \&set_part,
+		    "scratch-size=s" => \&set_part,
+		    "swap-size=s" => \&set_part,
+
+		    "kernel-from=s" => \&set_part,
+		    "filesys-from=s" => \&set_part,
+		    "swap-from=s" => \&set_part,
+
+		    "make-disk=s" => sub { $make_disk = $_[1];
+					   $tmp_disk = 0; },
+		    "disk=s" => sub { set_disk ($_[1]); },
+		    "loader=s" => \$loader_fn,
+
+		    "geometry=s" => \&set_geometry,
+		    "align=s" => \&set_align)
+	  or exit 1;
+    }
+
+    $sim = "bochs" if !defined $sim;
+    $debug = "none" if !defined $debug;
+    $vga = exists ($ENV{DISPLAY}) ? "window" : "none" if !defined $vga;
+
+    undef $timeout, print "warning: disabling timeout with --$debug\n"
+      if defined ($timeout) && $debug ne 'none';
+
+    print "warning: enabling serial port for -k or --kill-on-failure\n"
+      if $kill_on_failure && !$serial;
+
+    $align = "bochs",
+      print STDERR "warning: setting --align=bochs for Bochs support\n"
+	if $sim eq 'bochs' && defined ($align) && $align eq 'none';
+
+    $kill_on_failure = 0;
+}
+
+# usage($exitcode).
+# Prints a usage message and exits with $exitcode.
+sub usage {
+    my ($exitcode) = @_;
+    $exitcode = 1 unless defined $exitcode;
+    print <<'EOF';
+pintos, a utility for running Pintos in a simulator
+Usage: pintos [OPTION...] -- [ARGUMENT...]
+where each OPTION is one of the following options
+  and each ARGUMENT is passed to Pintos kernel verbatim.
+Simulator selection:
+  --bochs                  (default) Use Bochs as simulator
+  --qemu                   Use QEMU as simulator
+  --player                 Use VMware Player as simulator
+Debugger selection:
+  --no-debug               (default) No debugger
+  --monitor                Debug with simulator's monitor
+  --gdb                    Debug with gdb
+Display options: (default is both VGA and serial)
+  -v, --no-vga             No VGA display or keyboard
+  -s, --no-serial          No serial input or output
+  -t, --terminal           Display VGA in terminal (Bochs only)
+Timing options: (Bochs only)
+  -j SEED                  Randomize timer interrupts
+  -r, --realtime           Use realistic, not reproducible, timings
+Testing options:
+  -T, --timeout=N          Kill Pintos after N seconds CPU time or N*load_avg
+                           seconds wall-clock time (whichever comes first)
+  -k, --kill-on-failure    Kill Pintos a few seconds after a kernel or user
+                           panic, test failure, or triple fault
+Configuration options:
+  -m, --mem=N              Give Pintos N MB physical RAM (default: 4)
+File system commands:
+  -p, --put-file=HOSTFN    Copy HOSTFN into VM, by default under same name
+  -g, --get-file=GUESTFN   Copy GUESTFN out of VM, by default under same name
+  -a, --as=FILENAME        Specifies guest (for -p) or host (for -g) file name
+Partition options: (where PARTITION is one of: kernel filesys scratch swap)
+  --PARTITION=FILE         Use a copy of FILE for the given PARTITION
+  --PARTITION-size=SIZE    Create an empty PARTITION of the given SIZE in MB
+  --PARTITION-from=DISK    Use of a copy of the given PARTITION in DISK
+  (There is no --kernel-size, --scratch, or --scratch-from option.)
+Disk configuration options:
+  --make-disk=DISK         Name the new DISK and don't delete it after the run
+  --disk=DISK              Also use existing DISK (may be used multiple times)
+Advanced disk configuration options:
+  --loader=FILE            Use FILE as bootstrap loader (default: loader.bin)
+  --geometry=H,S           Use H head, S sector geometry (default: 16,63)
+  --geometry=zip           Use 64 head, 32 sector geometry for USB-ZIP boot
+                           (see http://syslinux.zytor.com/usbkey.php)
+  --align=bochs            Pad out disk to cylinder to support Bochs (default)
+  --align=full             Align partition boundaries to cylinder boundary to
+                           let fdisk guess correct geometry and quiet warnings
+  --align=none             Don't align partitions at all, to save space
+Other options:
+  -h, --help               Display this help message.
+EOF
+    exit $exitcode;
+}
+
+# Sets the simulator.
+sub set_sim {
+    my ($new_sim) = @_;
+    die "--$new_sim conflicts with --$sim\n"
+	if defined ($sim) && $sim ne $new_sim;
+    $sim = $new_sim;
+}
+
+# Sets the debugger.
+sub set_debug {
+    my ($new_debug) = @_;
+    die "--$new_debug conflicts with --$debug\n"
+	if $debug ne 'none' && $new_debug ne 'none' && $debug ne $new_debug;
+    $debug = $new_debug;
+}
+
+# Sets VGA output destination.
+sub set_vga {
+    my ($new_vga) = @_;
+    if (defined ($vga) && $vga ne $new_vga) {
+	print "warning: conflicting vga display options\n";
+    }
+    $vga = $new_vga;
+}
+
+# Sets randomized timer interrupts.
+sub set_jitter {
+    my ($new_jitter) = @_;
+    die "--realtime conflicts with --jitter\n" if defined $realtime;
+    die "different --jitter already defined\n"
+	if defined $jitter && $jitter != $new_jitter;
+    $jitter = $new_jitter;
+}
+
+# Sets real-time timer interrupts.
+sub set_realtime {
+    die "--realtime conflicts with --jitter\n" if defined $jitter;
+    $realtime = 1;
+}
+
+# add_file(\@list, $file)
+#
+# Adds [$file] to @list, which should be @puts or @gets.
+# Sets $as_ref to point to the added element.
+sub add_file {
+    my ($list, $file) = @_;
+    $as_ref = [$file];
+    push (@$list, $as_ref);
+}
+
+# Sets the guest/host name for the previous put/get.
+sub set_as {
+    my ($as) = @_;
+    die "-a (or --as) is only allowed after -p or -g\n" if !defined $as_ref;
+    die "Only one -a (or --as) is allowed after -p or -g\n"
+      if defined $as_ref->[1];
+    $as_ref->[1] = $as;
+}
+
+# Sets $disk as a disk to be included in the VM to run.
+sub set_disk {
+    my ($disk) = @_;
+
+    push (@disks, $disk);
+
+    my (%pt) = read_partition_table ($disk);
+    for my $role (keys %pt) {
+	die "can't have two sources for \L$role\E partition"
+	  if exists $parts{$role};
+	$parts{$role}{DISK} = $disk;
+	$parts{$role}{START} = $pt{$role}{START};
+	$parts{$role}{SECTORS} = $pt{$role}{SECTORS};
+    }
+}
+
+# Locates the files used to back each of the virtual disks,
+# and creates temporary disks.
+sub find_disks {
+    # Find kernel, if we don't already have one.
+    if (!exists $parts{KERNEL}) {
+	my $name = find_file ('kernel.bin');
+	die "Cannot find kernel\n" if !defined $name;
+	do_set_part ('KERNEL', 'file', $name);
+    }
+
+    # Try to find file system and swap disks, if we don't already have
+    # partitions.
+    if (!exists $parts{FILESYS}) {
+	my $name = find_file ('filesys.dsk');
+	set_disk ($name) if defined $name;
+    }
+    if (!exists $parts{SWAP}) {
+	my $name = find_file ('swap.dsk');
+	set_disk ($name) if defined $name;
+    }
+
+    # Warn about (potentially) missing partitions.
+    if (my ($project) = `pwd` =~ /\b(threads|userprog|vm|filesys)\b/) {
+	if ((grep ($project eq $_, qw (userprog vm filesys)))
+	    && !defined $parts{FILESYS}) {
+	    print STDERR "warning: it looks like you're running the $project ";
+	    print STDERR "project, but no file system partition is present\n";
+	}
+	if ($project eq 'vm' && !defined $parts{SWAP}) {
+	    print STDERR "warning: it looks like you're running the $project ";
+	    print STDERR "project, but no swap partition is present\n";
+	}
+    }
+
+    # Open disk handle.
+    my ($handle);
+    if (!defined $make_disk) {
+	($handle, $make_disk) = tempfile (UNLINK => $tmp_disk,
+					  SUFFIX => '.dsk');
+    } else {
+	die "$make_disk: already exists\n" if -e $make_disk;
+	open ($handle, '>', $make_disk) or die "$make_disk: create: $!\n";
+    }
+
+    # Prepare the arguments to pass to the Pintos kernel.
+    my (@args);
+    push (@args, shift (@kernel_args))
+      while @kernel_args && $kernel_args[0] =~ /^-/;
+    push (@args, 'extract') if @puts;
+    push (@args, @kernel_args);
+    push (@args, 'append', $_->[0]) foreach @gets;
+
+    # Make disk.
+    my (%disk);
+    our (@role_order);
+    for my $role (@role_order) {
+	my $p = $parts{$role};
+	next if !defined $p;
+	next if exists $p->{DISK};
+	$disk{$role} = $p;
+    }
+    $disk{DISK} = $make_disk;
+    $disk{HANDLE} = $handle;
+    $disk{ALIGN} = $align;
+    $disk{GEOMETRY} = %geometry;
+    $disk{FORMAT} = 'partitioned';
+    $disk{LOADER} = read_loader ($loader_fn);
+    $disk{ARGS} = \@args;
+    assemble_disk (%disk);
+
+    # Put the disk at the front of the list of disks.
+    unshift (@disks, $make_disk);
+    die "can't use more than " . scalar (@disks) . "disks\n" if @disks > 4;
+}
+
+# Prepare the scratch disk for gets and puts.
+sub prepare_scratch_disk {
+    return if !@gets && !@puts;
+
+    my ($p) = $parts{SCRATCH};
+    # Create temporary partition and write the files to put to it,
+    # then write an end-of-archive marker.
+    my ($part_handle, $part_fn) = tempfile (UNLINK => 1, SUFFIX => '.part');
+    put_scratch_file ($_->[0], defined $_->[1] ? $_->[1] : $_->[0],
+		      $part_handle, $part_fn)
+      foreach @puts;
+    write_fully ($part_handle, $part_fn, "\0" x 1024);
+
+    # Make sure the scratch disk is big enough to get big files
+    # and at least as big as any requested size.
+    my ($size) = round_up (max (@gets * 1024 * 1024, $p->{BYTES} || 0), 512);
+    extend_file ($part_handle, $part_fn, $size);
+    close ($part_handle);
+
+    if (exists $p->{DISK}) {
+	# Copy the scratch partition to the disk.
+	die "$p->{DISK}: scratch partition too small\n"
+	  if $p->{SECTORS} * 512 < $size;
+
+	my ($disk_handle);
+	open ($part_handle, '<', $part_fn) or die "$part_fn: open: $!\n";
+	open ($disk_handle, '+<', $p->{DISK}) or die "$p->{DISK}: open: $!\n";
+	my ($start) = $p->{START} * 512;
+	sysseek ($disk_handle, $start, SEEK_SET) == $start
+	  or die "$p->{DISK}: seek: $!\n";
+	copy_file ($part_handle, $part_fn, $disk_handle, $p->{DISK}, $size);
+	close ($disk_handle) or die "$p->{DISK}: close: $!\n";
+	close ($part_handle) or die "$part_fn: close: $!\n";
+    } else {
+	# Set $part_fn as the source for the scratch partition.
+	do_set_part ('SCRATCH', 'file', $part_fn);
+    }
+}
+
+# Read "get" files from the scratch disk.
+sub finish_scratch_disk {
+    return if !@gets;
+
+    # Open scratch partition.
+    my ($p) = $parts{SCRATCH};
+    my ($part_handle);
+    my ($part_fn) = $p->{DISK};
+    open ($part_handle, '<', $part_fn) or die "$part_fn: open: $!\n";
+    sysseek ($part_handle, $p->{START} * 512, SEEK_SET) == $p->{START} * 512
+      or die "$part_fn: seek: $!\n";
+
+    # Read each file.
+    # If reading fails, delete that file and all subsequent files, but
+    # don't die with an error, because that's a guest error not a host
+    # error.  (If we do exit with an error code, it fouls up the
+    # grading process.)  Instead, just make sure that the host file(s)
+    # we were supposed to retrieve is unlinked.
+    my ($ok) = 1;
+    my ($part_end) = ($p->{START} + $p->{SECTORS}) * 512;
+    foreach my $get (@gets) {
+	my ($name) = defined ($get->[1]) ? $get->[1] : $get->[0];
+	if ($ok) {
+	    my ($error) = get_scratch_file ($name, $part_handle, $part_fn);
+	    if (!$error && sysseek ($part_handle, 0, SEEK_CUR) > $part_end) {
+		$error = "$part_fn: scratch data overflows partition";
+	    }
+	    if ($error) {
+		print STDERR "getting $name failed ($error)\n";
+		$ok = 0;
+	    }
+	}
+	die "$name: unlink: $!\n" if !$ok && !unlink ($name) && !$!{ENOENT};
+    }
+}
+
+# mk_ustar_field($number, $size)
+#
+# Returns $number in a $size-byte numeric field in the format used by
+# the standard ustar archive header.
+sub mk_ustar_field {
+    my ($number, $size) = @_;
+    my ($len) = $size - 1;
+    my ($out) = sprintf ("%0${len}o", $number) . "\0";
+    die "$number: too large for $size-byte octal ustar field\n"
+      if length ($out) != $size;
+    return $out;
+}
+
+# calc_ustar_chksum($s)
+#
+# Calculates and returns the ustar checksum of 512-byte ustar archive
+# header $s.
+sub calc_ustar_chksum {
+    my ($s) = @_;
+    die if length ($s) != 512;
+    substr ($s, 148, 8, ' ' x 8);
+    return unpack ("%32a*", $s);
+}
+
+# put_scratch_file($src_file_name, $dst_file_name,
+#                  $disk_handle, $disk_file_name).
+#
+# Copies $src_file_name into $disk_handle for extraction as
+# $dst_file_name.  $disk_file_name is used for error messages.
+sub put_scratch_file {
+    my ($src_file_name, $dst_file_name, $disk_handle, $disk_file_name) = @_;
+
+    print "Copying $src_file_name to scratch partition...\n";
+
+    # ustar format supports up to 100 characters for a file name, and
+    # even longer names given some common properties, but our code in
+    # the Pintos kernel only supports at most 99 characters.
+    die "$dst_file_name: name too long (max 99 characters)\n"
+      if length ($dst_file_name) > 99;
+
+    # Compose and write ustar header.
+    stat $src_file_name or die "$src_file_name: stat: $!\n";
+    my ($size) = -s _;
+    my ($header) = (pack ("a100", $dst_file_name)	# name
+		    . mk_ustar_field (0644, 8)		# mode
+		    . mk_ustar_field (0, 8)		# uid
+		    . mk_ustar_field (0, 8)		# gid
+		    . mk_ustar_field ($size, 12)	# size
+		    . mk_ustar_field (1136102400, 12)	# mtime
+		    . (' ' x 8)				# chksum
+		    . '0'				# typeflag
+		    . ("\0" x 100)			# linkname
+		    . "ustar\0"				# magic
+		    . "00"				# version
+		    . "root" . ("\0" x 28)		# uname
+		    . "root" . ("\0" x 28)		# gname
+		    . "\0" x 8				# devmajor
+		    . "\0" x 8				# devminor
+		    . ("\0" x 155))			# prefix
+                    . "\0" x 12;			# pad to 512 bytes
+    substr ($header, 148, 8) = mk_ustar_field (calc_ustar_chksum ($header), 8);
+    write_fully ($disk_handle, $disk_file_name, $header);
+
+    # Copy file data.
+    my ($put_handle);
+    sysopen ($put_handle, $src_file_name, O_RDONLY)
+      or die "$src_file_name: open: $!\n";
+    copy_file ($put_handle, $src_file_name, $disk_handle, $disk_file_name,
+	       $size);
+    die "$src_file_name: changed size while being read\n"
+      if $size != -s $put_handle;
+    close ($put_handle);
+
+    # Round up disk data to beginning of next sector.
+    write_fully ($disk_handle, $disk_file_name, "\0" x (512 - $size % 512))
+      if $size % 512;
+}
+
+# get_scratch_file($get_file_name, $disk_handle, $disk_file_name)
+#
+# Copies from $disk_handle to $get_file_name (which is created).
+# $disk_file_name is used for error messages.
+# Returns 1 if successful, 0 on failure.
+sub get_scratch_file {
+    my ($get_file_name, $disk_handle, $disk_file_name) = @_;
+
+    print "Copying $get_file_name out of $disk_file_name...\n";
+
+    # Read ustar header sector.
+    my ($header) = read_fully ($disk_handle, $disk_file_name, 512);
+    return "scratch disk tar archive ends unexpectedly"
+      if $header eq ("\0" x 512);
+
+    # Verify magic numbers.
+    return "corrupt ustar signature" if substr ($header, 257, 6) ne "ustar\0";
+    return "invalid ustar version" if substr ($header, 263, 2) ne '00';
+
+    # Verify checksum.
+    my ($chksum) = oct (unpack ("Z*", substr ($header, 148, 8)));
+    my ($correct_chksum) = calc_ustar_chksum ($header);
+    return "checksum mismatch" if $chksum != $correct_chksum;
+
+    # Get type.
+    my ($typeflag) = substr ($header, 156, 1);
+    return "not a regular file" if $typeflag ne '0' && $typeflag ne "\0";
+
+    # Get size.
+    my ($size) = oct (unpack ("Z*", substr ($header, 124, 12)));
+    return "bad size $size\n" if $size < 0;
+
+    # Copy file data.
+    my ($get_handle);
+    sysopen ($get_handle, $get_file_name, O_WRONLY | O_CREAT, 0666)
+      or die "$get_file_name: create: $!\n";
+    copy_file ($disk_handle, $disk_file_name, $get_handle, $get_file_name,
+	       $size);
+    close ($get_handle);
+
+    # Skip forward in disk up to beginning of next sector.
+    read_fully ($disk_handle, $disk_file_name, 512 - $size % 512)
+      if $size % 512;
+
+    return 0;
+}
+
+# Running simulators.
+
+# Runs the selected simulator.
+sub run_vm {
+    if ($sim eq 'bochs') {
+	run_bochs ();
+    } elsif ($sim eq 'qemu') {
+	run_qemu ();
+    } elsif ($sim eq 'player') {
+	run_player ();
+    } else {
+	die "unknown simulator `$sim'\n";
+    }
+}
+
+# Runs Bochs.
+sub run_bochs {
+    # Select Bochs binary based on the chosen debugger.
+    my ($bin) = $debug eq 'monitor' ? 'bochs-dbg' : 'bochs';
+
+    my ($squish_pty);
+    if ($serial) {
+	$squish_pty = find_in_path ("squish-pty");
+	print "warning: can't find squish-pty, so terminal input will fail\n"
+	  if !defined $squish_pty;
+    }
+
+    # Write bochsrc.txt configuration file.
+    open (BOCHSRC, ">", "bochsrc.txt") or die "bochsrc.txt: create: $!\n";
+    print BOCHSRC <<EOF;
+romimage: file=\$BXSHARE/BIOS-bochs-latest
+vgaromimage: file=\$BXSHARE/VGABIOS-lgpl-latest
+boot: disk
+cpu: ips=1000000
+megs: $mem
+log: bochsout.txt
+panic: action=fatal
+# For older bochs:
+#user_shortcut: keys=ctrlaltdel
+# For more recent bochs:
+keyboard: user_shortcut=ctrl-alt-del
+EOF
+    print BOCHSRC "gdbstub: enabled=1, port=$gdb_port\n" if $debug eq 'gdb';
+    print BOCHSRC "clock: sync=", $realtime ? 'realtime' : 'none',
+      ", time0=0\n";
+    print BOCHSRC "ata1: enabled=1, ioaddr1=0x170, ioaddr2=0x370, irq=15\n"
+      if @disks > 2;
+    print_bochs_disk_line ("ata0-master", $disks[0]);
+    print_bochs_disk_line ("ata0-slave", $disks[1]);
+    print_bochs_disk_line ("ata1-master", $disks[2]);
+    print_bochs_disk_line ("ata1-slave", $disks[3]);
+    if ($vga ne 'terminal') {
+	if ($serial) {
+	    my $mode = defined ($squish_pty) ? "term" : "file";
+	    print BOCHSRC "com1: enabled=1, mode=$mode, dev=/dev/stdout\n";
+	}
+	print BOCHSRC "display_library: nogui\n" if $vga eq 'none';
+    } else {
+	print BOCHSRC "display_library: term\n";
+    }
+    close (BOCHSRC);
+
+    # Compose Bochs command line.
+    my (@cmd) = ($bin, '-q');
+    unshift (@cmd, $squish_pty) if defined $squish_pty;
+    push (@cmd, '-j', $jitter) if defined $jitter;
+
+    # Run Bochs.
+    print join (' ', @cmd), "\n";
+    my ($exit) = xsystem (@cmd);
+    if (WIFEXITED ($exit)) {
+	# Bochs exited normally.
+	# Ignore the exit code; Bochs normally exits with status 1,
+	# which is weird.
+    } elsif (WIFSIGNALED ($exit)) {
+	die "Bochs died with signal ", WTERMSIG ($exit), "\n";
+    } else {
+	die "Bochs died: code $exit\n";
+    }
+}
+
+sub print_bochs_disk_line {
+    my ($device, $disk) = @_;
+    if (defined $disk) {
+	my (%geom) = disk_geometry ($disk);
+	print BOCHSRC "$device: type=disk, path=$disk, mode=flat, ";
+	print BOCHSRC "cylinders=$geom{C}, heads=$geom{H}, spt=$geom{S}, ";
+	print BOCHSRC "translation=none\n";
+    }
+}
+
+# Runs QEMU.
+sub run_qemu {
+    print "warning: qemu doesn't support --terminal\n"
+      if $vga eq 'terminal';
+    print "warning: qemu doesn't support jitter\n"
+      if defined $jitter;
+    my (@cmd) = ('qemu-system-i386');
+    push (@cmd, '-device', 'isa-debug-exit');
+
+    my ($i);
+    for ($i = 0; $i < 4; $i++) {
+	if (defined $disks[$i]) {
+	    push (@cmd, '-drive');
+	    push (@cmd, "file=$disks[$i],format=raw,index=$i,media=disk");
+	}
+    }
+#    push (@cmd, '-hda', $disks[0]) if defined $disks[0];
+#    push (@cmd, '-hdb', $disks[1]) if defined $disks[1];
+#    push (@cmd, '-hdc', $disks[2]) if defined $disks[2];
+#    push (@cmd, '-hdd', $disks[3]) if defined $disks[3];
+    push (@cmd, '-m', $mem);
+    push (@cmd, '-net', 'none');
+    push (@cmd, '-nographic') if $vga eq 'none';
+    push (@cmd, '-serial', 'stdio') if $serial && $vga ne 'none';
+    push (@cmd, '-S') if $debug eq 'monitor';
+    push (@cmd, '-gdb', "tcp::$gdb_port", '-S') if $debug eq 'gdb';
+    push (@cmd, '-monitor', 'null') if $vga eq 'none' && $debug eq 'none';
+    run_command (@cmd);
+}
+
+# player_unsup($flag)
+#
+# Prints a message that $flag is unsupported by VMware Player.
+sub player_unsup {
+    my ($flag) = @_;
+    print "warning: no support for $flag with VMware Player\n";
+}
+
+# Runs VMware Player.
+sub run_player {
+    player_unsup ("--$debug") if $debug ne 'none';
+    player_unsup ("--no-vga") if $vga eq 'none';
+    player_unsup ("--terminal") if $vga eq 'terminal';
+    player_unsup ("--jitter") if defined $jitter;
+    player_unsup ("--timeout"), undef $timeout if defined $timeout;
+    player_unsup ("--kill-on-failure"), undef $kill_on_failure
+      if defined $kill_on_failure;
+
+    $mem = round_up ($mem, 4);	# Memory must be multiple of 4 MB.
+
+    open (VMX, ">", "pintos.vmx") or die "pintos.vmx: create: $!\n";
+    chmod 0777 & ~umask, "pintos.vmx";
+    print VMX <<EOF;
+#! /usr/bin/vmware -G
+config.version = 8
+guestOS = "linux"
+memsize = $mem
+floppy0.present = FALSE
+usb.present = FALSE
+sound.present = FALSE
+gui.exitAtPowerOff = TRUE
+gui.exitOnCLIHLT = TRUE
+gui.powerOnAtStartUp = TRUE
+EOF
+
+    print VMX <<EOF if $serial;
+serial0.present = TRUE
+serial0.fileType = "pipe"
+serial0.fileName = "pintos.socket"
+serial0.pipe.endPoint = "client"
+serial0.tryNoRxLoss = "TRUE"
+EOF
+
+    for (my ($i) = 0; $i < 4; $i++) {
+	my ($dsk) = $disks[$i];
+	last if !defined $dsk;
+
+	my ($device) = "ide" . int ($i / 2) . ":" . ($i % 2);
+	my ($pln) = "$device.pln";
+	print VMX <<EOF;
+
+$device.present = TRUE
+$device.deviceType = "plainDisk"
+$device.fileName = "$pln"
+EOF
+
+	open (URANDOM, '<', '/dev/urandom') or die "/dev/urandom: open: $!\n";
+	my ($bytes);
+	sysread (URANDOM, $bytes, 4) == 4 or die "/dev/urandom: read: $!\n";
+	close (URANDOM);
+	my ($cid) = unpack ("L", $bytes);
+
+	my (%geom) = disk_geometry ($dsk);
+	open (PLN, ">", $pln) or die "$pln: create: $!\n";
+	print PLN <<EOF;
+version=1
+CID=$cid
+parentCID=ffffffff
+createType="monolithicFlat"
+
+RW $geom{CAPACITY} FLAT "$dsk" 0
+
+# The Disk Data Base
+#DDB
+
+ddb.adapterType = "ide"
+ddb.virtualHWVersion = "4"
+ddb.toolsVersion = "2"
+ddb.geometry.cylinders = "$geom{C}"
+ddb.geometry.heads = "$geom{H}"
+ddb.geometry.sectors = "$geom{S}"
+EOF
+	close (PLN);
+    }
+    close (VMX);
+
+    my ($squish_unix);
+    if ($serial) {
+	$squish_unix = find_in_path ("squish-unix");
+	print "warning: can't find squish-unix, so terminal input ",
+	  "and output will fail\n" if !defined $squish_unix;
+    }
+
+    my ($vmx) = getcwd () . "/pintos.vmx";
+    my (@cmd) = ("vmplayer", $vmx);
+    unshift (@cmd, $squish_unix, "pintos.socket") if $squish_unix;
+    print join (' ', @cmd), "\n";
+    xsystem (@cmd);
+}
+
+# Disk utilities.
+
+sub extend_file {
+    my ($handle, $file_name, $size) = @_;
+    if (-s ($handle) < $size) {
+	sysseek ($handle, $size - 1, 0) == $size - 1
+	  or die "$file_name: seek: $!\n";
+	syswrite ($handle, "\0") == 1
+	  or die "$file_name: write: $!\n";
+    }
+}
+
+# disk_geometry($file)
+#
+# Examines $file and returns a valid IDE disk geometry for it, as a
+# hash.
+sub disk_geometry {
+    my ($file) = @_;
+    my ($size) = -s $file;
+    die "$file: stat: $!\n" if !defined $size;
+    die "$file: size $size not a multiple of 512 bytes\n" if $size % 512;
+    my ($cyl_size) = 512 * 16 * 63;
+    my ($cylinders) = ceil ($size / $cyl_size);
+
+    return (CAPACITY => $size / 512,
+	    C => $cylinders,
+	    H => 16,
+	    S => 63);
+}
+
+# Subprocess utilities.
+
+# run_command(@args)
+#
+# Runs xsystem(@args).
+# Also prints the command it's running and checks that it succeeded.
+sub run_command {
+    print join (' ', @_), "\n";
+    die "command failed\n" if xsystem (@_);
+}
+
+# xsystem(@args)
+#
+# Creates a subprocess via exec(@args) and waits for it to complete.
+# Relays common signals to the subprocess.
+# If $timeout is set then the subprocess will be killed after that long.
+sub xsystem {
+    # QEMU turns off local echo and does not restore it if killed by a signal.
+    # We compensate by restoring it ourselves.
+    my $cleanup = sub {};
+    if (isatty (0)) {
+	my $termios = POSIX::Termios->new;
+	$termios->getattr (0);
+	$cleanup = sub { $termios->setattr (0, &POSIX::TCSANOW); }
+    }
+
+    # Create pipe for filtering output.
+    pipe (my $in, my $out) or die "pipe: $!\n" if $kill_on_failure;
+
+    my ($pid) = fork;
+    if (!defined ($pid)) {
+	# Fork failed.
+	die "fork: $!\n";
+    } elsif (!$pid) {
+	# Running in child process.
+	dup2 (fileno ($out), STDOUT_FILENO) or die "dup2: $!\n"
+	  if $kill_on_failure;
+	exec_setitimer (@_);
+    } else {
+	# Running in parent process.
+	close $out if $kill_on_failure;
+
+	my ($cause);
+	local $SIG{ALRM} = sub { timeout ($pid, $cause, $cleanup); };
+	local $SIG{INT} = sub { relay_signal ($pid, "INT", $cleanup); };
+	local $SIG{TERM} = sub { relay_signal ($pid, "TERM", $cleanup); };
+	alarm ($timeout * get_load_average () + 1) if defined ($timeout);
+
+	if ($kill_on_failure) {
+	    # Filter output.
+	    my ($buf) = "";
+	    my ($boots) = 0;
+	    local ($|) = 1;
+	    for (;;) {
+		if (waitpid ($pid, WNOHANG) != 0) {
+		    # Subprocess died.  Pass through any remaining data.
+		    do { print $buf } while sysread ($in, $buf, 4096) > 0;
+		    last;
+		}
+
+		# Read and print out pipe data.
+		my ($len) = length ($buf);
+		my ($n_read) = sysread ($in, $buf, 4096, $len);
+		waitpid ($pid, 0), last if !defined ($n_read) || $n_read <= 0;
+		print substr ($buf, $len);
+
+		# Remove full lines from $buf and scan them for keywords.
+		while ((my $idx = index ($buf, "\n")) >= 0) {
+		    local $_ = substr ($buf, 0, $idx + 1, '');
+		    next if defined ($cause);
+		    if (/(Kernel PANIC|User process ABORT)/ ) {
+			$cause = "\L$1\E";
+			alarm (5);
+		    } elsif (/Pintos booting/ && ++$boots > 1) {
+			$cause = "triple fault";
+			alarm (5);
+		    } elsif (/FAILED/) {
+			$cause = "test failure";
+			alarm (5);
+		    }
+		}
+	    }
+	} else {
+	    waitpid ($pid, 0);
+	}
+	alarm (0);
+	&$cleanup ();
+
+	if (WIFSIGNALED ($?) && WTERMSIG ($?) == SIGVTALRM_number ()) {
+	    seek (STDOUT, 0, 2);
+	    print "\nTIMEOUT after $timeout seconds of host CPU time\n";
+	    exit 0;
+	}
+
+        # Kind of a gross hack, because qemu's isa-debug-exit device
+        # only allows odd-numbered exit values, so we can't exit
+        # cleanly with 0.  We use exit status 0x63 as an alternate
+        # "clean" exit status.
+	return ($? != 0x6300) && $?;
+    }
+}
+
+# relay_signal($pid, $signal, &$cleanup)
+#
+# Relays $signal to $pid and then reinvokes it for us with the default
+# handler.  Also cleans up temporary files and invokes $cleanup.
+sub relay_signal {
+    my ($pid, $signal, $cleanup) = @_;
+    kill $signal, $pid;
+    eval { File::Temp::cleanup() };	# Not defined in old File::Temp.
+    &$cleanup ();
+    $SIG{$signal} = 'DEFAULT';
+    kill $signal, getpid ();
+}
+
+# timeout($pid, $cause, &$cleanup)
+#
+# Interrupts $pid and dies with a timeout error message,
+# after invoking $cleanup.
+sub timeout {
+    my ($pid, $cause, $cleanup) = @_;
+    kill "INT", $pid;
+    waitpid ($pid, 0);
+    &$cleanup ();
+    seek (STDOUT, 0, 2);
+    if (!defined ($cause)) {
+	my ($load_avg) = `uptime` =~ /(load average:.*)$/i;
+	print "\nTIMEOUT after ", time () - $start_time,
+	  " seconds of wall-clock time";
+	print  " - $load_avg" if defined $load_avg;
+	print "\n";
+    } else {
+	print "Simulation terminated due to $cause.\n";
+    }
+    exit 0;
+}
+
+# Returns the system load average over the last minute.
+# If the load average is less than 1.0 or cannot be determined, returns 1.0.
+sub get_load_average {
+    my ($avg) = `uptime` =~ /load average:\s*([^,]+),/;
+    return $avg >= 1.0 ? $avg : 1.0;
+}
+
+# Calls setitimer to set a timeout, then execs what was passed to us.
+sub exec_setitimer {
+    if (defined $timeout) {
+	if ($^V ge 5.8.0) {
+	    eval "
+              use Time::HiRes qw(setitimer ITIMER_VIRTUAL);
+              setitimer (ITIMER_VIRTUAL, $timeout, 0);
+            ";
+	} else {
+	    { exec ("setitimer-helper", $timeout, @_); };
+	    exit 1 if !$!{ENOENT};
+	    print STDERR "warning: setitimer-helper is not installed, so ",
+	      "CPU time limit will not be enforced\n";
+	}
+    }
+    exec (@_);
+    exit (1);
+}
+
+sub SIGVTALRM_number {
+    use Config;
+    my $i = 0;
+    foreach my $name (split(' ', $Config{sig_name})) {
+	return $i if $name eq 'VTALRM';
+	$i++;
+    }
+    return 0;
+}
+
+# find_in_path ($program)
+#
+# Searches for $program in $ENV{PATH}.
+# Returns $program if found, otherwise undef.
+sub find_in_path {
+    my ($program) = @_;
+    -x "$_/$program" and return $program foreach split (':', $ENV{PATH});
+    return;
+}
diff --git a/src/utils/pintos-gdb b/src/utils/pintos-gdb
new file mode 100644
index 0000000..4ef38d3
--- /dev/null
+++ b/src/utils/pintos-gdb
@@ -0,0 +1,20 @@
+#! /bin/sh
+
+# Path to GDB macros file.  Customize for your site.
+GDBMACROS=/usr/class/cs140/pintos/pintos/src/misc/gdb-macros
+
+# Choose correct GDB.
+if command -v i386-elf-gdb >/dev/null 2>&1; then
+	GDB=i386-elf-gdb
+else
+	GDB=gdb
+fi
+
+# Run GDB.
+if test -f "$GDBMACROS"; then
+	exec $GDB -x "$GDBMACROS" "$@"
+else
+	echo "*** $GDBMACROS does not exist ***"
+	echo "*** Pintos GDB macros will not be available ***"
+	exec $GDB "$@"
+fi
diff --git a/src/utils/pintos-mkdisk b/src/utils/pintos-mkdisk
new file mode 100644
index 0000000..87b1563
--- /dev/null
+++ b/src/utils/pintos-mkdisk
@@ -0,0 +1,134 @@
+#! /usr/bin/perl
+
+use strict;
+use warnings;
+use POSIX;
+use Getopt::Long qw(:config bundling);
+use Fcntl 'SEEK_SET';
+
+# Read Pintos.pm from the same directory as this program.
+BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; }
+
+our ($disk_fn);			# Output disk file name.
+our (%parts);			# Partitions.
+our ($format);			# "partitioned" (default) or "raw"
+our (%geometry);		# IDE disk geometry.
+our ($align);			# Align partitions on cylinders?
+our ($loader_fn);		# File name of loader.
+our ($include_loader);		# Include loader?
+our (@kernel_args);		# Kernel arguments.
+
+if (grep ($_ eq '--', @ARGV)) {
+    @kernel_args = @ARGV;
+    @ARGV = ();
+    while ((my $arg = shift (@kernel_args)) ne '--') {
+	push (@ARGV, $arg);
+    }
+}
+
+GetOptions ("h|help" => sub { usage (0); },
+
+	    "kernel=s" => \&set_part,
+	    "filesys=s" => \&set_part,
+	    "scratch=s" => \&set_part,
+	    "swap=s" => \&set_part,
+
+	    "filesys-size=s" => \&set_part,
+	    "scratch-size=s" => \&set_part,
+	    "swap-size=s" => \&set_part,
+
+	    "kernel-from=s" => \&set_part,
+	    "filesys-from=s" => \&set_part,
+	    "scratch-from=s" => \&set_part,
+	    "swap-from=s" => \&set_part,
+
+	    "format=s" => \$format,
+	    "loader:s" => \&set_loader,
+	    "no-loader" => \&set_no_loader,
+	    "geometry=s" => \&set_geometry,
+	    "align=s" => \&set_align)
+  or exit 1;
+usage (1) if @ARGV != 1;
+
+$disk_fn = $ARGV[0];
+die "$disk_fn: already exists\n" if -e $disk_fn;
+
+# Sets the loader to copy to the MBR.
+sub set_loader {
+    die "can't specify both --loader and --no-loader\n"
+      if defined ($include_loader) && !$include_loader;
+    $include_loader = 1;
+    $loader_fn = $_[1] if $_[1] ne '';
+}
+
+# Disables copying a loader to the MBR.
+sub set_no_loader {
+    die "can't specify both --loader and --no-loader\n"
+      if defined ($include_loader) && $include_loader;
+    $include_loader = 0;
+}
+
+# Figure out whether to include a loader.
+$include_loader = exists ($parts{KERNEL}) && $format eq 'partitioned'
+  if !defined ($include_loader);
+die "can't write loader to raw disk\n" if $include_loader && $format eq 'raw';
+die "can't write command-line arguments without --loader or --kernel\n"
+  if @kernel_args && !$include_loader;
+print STDERR "warning: --loader only makes sense without --kernel "
+  . "if this disk will be used to load a kernel from another disk\n"
+  if $include_loader && !exists ($parts{KERNEL});
+
+# Open disk.
+my ($disk_handle);
+open ($disk_handle, '>', $disk_fn) or die "$disk_fn: create: $!\n";
+
+# Read loader.
+my ($loader);
+$loader = read_loader ($loader_fn) if $include_loader;
+
+# Write disk.
+my (%disk) = %parts;
+$disk{DISK} = $disk_fn;
+$disk{HANDLE} = $disk_handle;
+$disk{ALIGN} = $align;
+$disk{GEOMETRY} = %geometry;
+$disk{FORMAT} = $format;
+$disk{LOADER} = $loader;
+$disk{ARGS} = \@kernel_args;
+assemble_disk (%disk);
+
+# Done.
+exit 0;
+
+sub usage {
+    print <<'EOF';
+pintos-mkdisk, a utility for creating Pintos virtual disks
+Usage: pintos-mkdisk [OPTIONS] DISK [-- ARGUMENT...]
+where DISK is the virtual disk to create,
+      each ARGUMENT is inserted into the command line written to DISK,
+  and each OPTION is one of the following options.
+Partition options: (where PARTITION is one of: kernel filesys scratch swap)
+  --PARTITION=FILE         Use a copy of FILE for the given PARTITION
+  --PARTITION-size=SIZE    Create an empty PARTITION of the given SIZE in MB
+  --PARTITION-from=DISK    Use of a copy of the given PARTITION in DISK
+  (There is no --kernel-size option.)
+Output disk options:
+  --format=partitioned     Write partition table to output (default)
+  --format=raw             Do not write partition table to output
+  (Pintos can only use partitioned disks.)
+Partitioned format output options:
+  --loader[=FILE]          Get bootstrap loader from FILE (default: loader.bin
+                           if --kernel option is specified, empty otherwise)
+  --no-loader              Do not include a bootstrap loader
+  --geometry=H,S           Use H head, S sector geometry (default: 16, 63)
+  --geometry=zip           Use 64 head, 32 sector geometry for USB-ZIP boot
+                           per http://syslinux.zytor.com/usbkey.php
+  --align=bochs            Round size to cylinder for Bochs support (default)
+  --align=full             Align partition boundaries to cylinder boundary to
+                           let fdisk guess correct geometry and quiet warnings
+  --align=none             Don't align partitions at all, to save space
+Other options:
+  -h, --help               Display this help message.
+EOF
+    exit ($_[0]);
+}
diff --git a/src/utils/pintos-set-cmdline b/src/utils/pintos-set-cmdline
new file mode 100644
index 0000000..8c8f702
--- /dev/null
+++ b/src/utils/pintos-set-cmdline
@@ -0,0 +1,42 @@
+#! /usr/bin/perl -w
+
+use strict;
+use Fcntl 'SEEK_SET';
+
+# Read Pintos.pm from the same directory as this program.
+BEGIN { my $self = $0; $self =~ s%/+[^/]*$%%; require "$self/Pintos.pm"; }
+
+# Get command-line arguments.
+usage (0) if @ARGV == 1 && $ARGV[0] eq '--help';
+usage (1) if @ARGV < 2 || $ARGV[1] ne '--';
+my ($disk, undef, @kernel_args) = @ARGV;
+
+# Open disk.
+my ($handle);
+open ($handle, '+<', $disk) or die "$disk: open: $!\n";
+
+# Check that it's a partitioned disk with a Pintos loader.
+my ($buffer) = read_fully ($handle, $disk, 512);
+unpack ("x510 v", $buffer) == 0xaa55 or die "$disk: not a partitioned disk\n";
+$buffer =~ /Pintos/ or die "$disk: does not contain Pintos loader\n";
+
+# Write the command line.
+our ($LOADER_SIZE);
+sysseek ($handle, $LOADER_SIZE, SEEK_SET) == $LOADER_SIZE
+  or die "$disk: seek: $!\n";
+write_fully ($handle, $disk, make_kernel_command_line (@kernel_args));
+
+# Close disk.
+close ($handle) or die "$disk: close: $!\n";
+
+exit 0;
+
+sub usage {
+    print <<'EOF';
+pintos-set-cmdline, a utility for changing the command line in Pintos disks
+Usage: pintos-set-cmdline DISK -- [ARGUMENT...]
+where DISK is a bootable disk containing a Pintos loader
+  and each ARGUMENT is inserted into the command line written to DISK.
+EOF
+    exit ($_[0]);
+}
diff --git a/src/utils/setitimer-helper.c b/src/utils/setitimer-helper.c
new file mode 100644
index 0000000..772d736
--- /dev/null
+++ b/src/utils/setitimer-helper.c
@@ -0,0 +1,49 @@
+#include <errno.h>
+#include <limits.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+int
+main (int argc, char *argv[]) 
+{
+  const char *program_name = argv[0];
+  double timeout;
+
+  if (argc < 3)
+    {
+      fprintf (stderr,
+               "setitimer-helper: runs a program with a virtual CPU limit\n"
+               "usage: %s TIMEOUT PROGRAM [ARG...]\n"
+               "  where TIMEOUT is the virtual CPU limit, in seconds,\n"
+               "    and remaining arguments specify the program to run\n"
+               "    and its argument.\n",
+               program_name);
+      return EXIT_FAILURE;
+    }
+
+  timeout = strtod (argv[1], NULL);
+  if (timeout >= 0.0 && timeout < LONG_MAX)
+    {
+      struct itimerval it;
+
+      it.it_interval.tv_sec = 0;
+      it.it_interval.tv_usec = 0;
+      it.it_value.tv_sec = timeout;
+      it.it_value.tv_usec = (timeout - floor (timeout)) * 1000000;
+      if (setitimer (ITIMER_VIRTUAL, &it, NULL) < 0)
+        fprintf (stderr, "%s: setitimer: %s\n",
+                 program_name, strerror (errno));
+    }
+  else
+    fprintf (stderr, "%s: invalid timeout value \"%s\"\n",
+             program_name, argv[1]);
+  
+  execvp (argv[2], &argv[2]);
+  fprintf (stderr, "%s: couldn't exec \"%s\": %s\n",
+           program_name, argv[2], strerror (errno));
+  return EXIT_FAILURE;
+}
diff --git a/src/utils/squish-pty.c b/src/utils/squish-pty.c
new file mode 100644
index 0000000..e81043d
--- /dev/null
+++ b/src/utils/squish-pty.c
@@ -0,0 +1,351 @@
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+static void
+fail_io (const char *msg, ...)
+     __attribute__ ((noreturn))
+     __attribute__ ((format (printf, 1, 2)));
+
+/* Prints MSG, formatting as with printf(),
+   plus an error message based on errno,
+   and exits. */
+static void
+fail_io (const char *msg, ...)
+{
+  va_list args;
+
+  va_start (args, msg);
+  vfprintf (stderr, msg, args);
+  va_end (args);
+
+  if (errno != 0)
+    fprintf (stderr, ": %s", strerror (errno));
+  putc ('\n', stderr);
+  exit (EXIT_FAILURE);
+}
+
+/* If FD is a terminal, configures it for noncanonical input mode
+   with VMIN and VTIME set as indicated.
+   If FD is not a terminal, has no effect. */
+static void
+make_noncanon (int fd, int vmin, int vtime)
+{
+  if (isatty (fd)) 
+    {
+      struct termios termios;
+      if (tcgetattr (fd, &termios) < 0)
+        fail_io ("tcgetattr");
+      termios.c_lflag &= ~(ICANON | ECHO);
+      termios.c_cc[VMIN] = vmin;
+      termios.c_cc[VTIME] = vtime;
+      if (tcsetattr (fd, TCSANOW, &termios) < 0)
+        fail_io ("tcsetattr");
+    }
+}
+
+/* Make FD non-blocking if NONBLOCKING is true,
+   or blocking if NONBLOCKING is false. */
+static void
+make_nonblocking (int fd, bool nonblocking) 
+{
+  int flags = fcntl (fd, F_GETFL);
+  if (flags < 0)
+    fail_io ("fcntl");
+  if (nonblocking)
+    flags |= O_NONBLOCK;
+  else
+    flags &= ~O_NONBLOCK;
+  if (fcntl (fd, F_SETFL, flags) < 0)
+    fail_io ("fcntl");
+}
+
+/* Handle a read or write on *FD, which is the pty if FD_IS_PTY
+   is true, that returned end-of-file or error indication RETVAL.
+   The system call is named CALL, for use in error messages.
+   Sets *FD to -1 if the fd is no longer readable or writable. */
+static void
+handle_error (ssize_t retval, int *fd, bool fd_is_pty, const char *call)
+{
+  if (fd_is_pty)
+    {
+      if (retval < 0)
+        {
+          if (errno == EIO)
+            {
+              /* Slave side of pty has been closed. */
+              *fd = -1;
+            }
+          else
+            fail_io (call); 
+        }
+    }
+  else 
+    {
+      if (retval == 0)
+        {
+          close (*fd);
+          *fd = -1;
+        }
+      else
+        fail_io (call);
+    }
+}
+
+/* Copies data from stdin to PTY and from PTY to stdout until no
+   more data can be read or written. */
+static void
+relay (int pty, int dead_child_fd) 
+{
+  struct pipe 
+    {
+      int in, out;
+      char buf[BUFSIZ];
+      size_t size, ofs;
+      bool active;
+    };
+  struct pipe pipes[2];
+
+  /* Make PTY, stdin, and stdout non-blocking. */
+  make_nonblocking (pty, true);
+  make_nonblocking (STDIN_FILENO, true);
+  make_nonblocking (STDOUT_FILENO, true);
+
+  /* Configure noncanonical mode on PTY and stdin to avoid
+     waiting for end-of-line.  We want to minimize context
+     switching on PTY (for efficiency) and minimize latency on
+     stdin to avoid a laggy user experience. */
+  make_noncanon (pty, 16, 1);
+  make_noncanon (STDIN_FILENO, 1, 0);
+
+  memset (pipes, 0, sizeof pipes);
+  pipes[0].in = STDIN_FILENO;
+  pipes[0].out = pty;
+  pipes[1].in = pty;
+  pipes[1].out = STDOUT_FILENO;
+  
+  while (pipes[1].in != -1)
+    {
+      fd_set read_fds, write_fds;
+      int retval;
+      int i;
+
+      FD_ZERO (&read_fds);
+      FD_ZERO (&write_fds);
+      for (i = 0; i < 2; i++)
+        {
+          struct pipe *p = &pipes[i];
+
+          /* Don't do anything with the stdin->pty pipe until we
+             have some data for the pty->stdout pipe.  If we get
+             too eager, Bochs will throw away our input. */
+          if (i == 0 && !pipes[1].active)
+            continue;
+          
+          if (p->in != -1 && p->size + p->ofs < sizeof p->buf)
+            FD_SET (p->in, &read_fds);
+          if (p->out != -1 && p->size > 0)
+            FD_SET (p->out, &write_fds); 
+        }
+      FD_SET (dead_child_fd, &read_fds);
+
+      do 
+        {
+          retval = select (FD_SETSIZE, &read_fds, &write_fds, NULL, NULL); 
+        }
+      while (retval < 0 && errno == EINTR);
+      if (retval < 0) 
+        fail_io ("select");
+
+      if (FD_ISSET (dead_child_fd, &read_fds))
+        break;
+
+      for (i = 0; i < 2; i++) 
+        {
+          struct pipe *p = &pipes[i];
+          if (p->in != -1 && FD_ISSET (p->in, &read_fds))
+            {
+              ssize_t n = read (p->in, p->buf + p->ofs + p->size,
+                                sizeof p->buf - p->ofs - p->size);
+              if (n > 0) 
+                {
+                  p->active = true;
+                  p->size += n;
+                  if (p->size == BUFSIZ && p->ofs != 0)
+                    {
+                      memmove (p->buf, p->buf + p->ofs, p->size);
+                      p->ofs = 0;
+                    }
+                }
+              else
+                handle_error (n, &p->in, p->in == pty, "read");
+            }
+          if (p->out != -1 && FD_ISSET (p->out, &write_fds)) 
+            {
+              ssize_t n = write (p->out, p->buf + p->ofs, p->size);
+              if (n > 0) 
+                {
+                  p->ofs += n;
+                  p->size -= n;
+                  if (p->size == 0)
+                    p->ofs = 0;
+                }
+              else
+                handle_error (n, &p->out, p->out == pty, "write");
+            }
+        }
+    }
+
+    if (pipes[1].out == -1)
+      return;
+
+    make_nonblocking (STDOUT_FILENO, false);
+    for (;;)
+      {
+        struct pipe *p = &pipes[1];
+        ssize_t n;
+
+        /* Write buffer. */
+        while (p->size > 0) 
+          {
+            n = write (p->out, p->buf + p->ofs, p->size);
+            if (n < 0)
+              fail_io ("write");
+            else if (n == 0)
+              fail_io ("zero-length write");
+            p->ofs += n;
+            p->size -= n;
+          }
+        p->ofs = 0;
+
+        p->size = n = read (p->in, p->buf, sizeof p->buf);
+        if (n <= 0)
+          return;
+      }
+}
+
+static int dead_child_fd;
+
+static void
+sigchld_handler (int signo __attribute__ ((unused))) 
+{
+  if (write (dead_child_fd, "", 1) < 0)
+    _exit (1);
+}
+
+int
+main (int argc __attribute__ ((unused)), char *argv[])
+{
+  int master, slave;
+  char *name;
+  pid_t pid;
+  struct sigaction sa;
+  int pipe_fds[2];
+  struct itimerval zero_itimerval, old_itimerval;
+
+  if (argc < 2) 
+    {
+      fprintf (stderr,
+               "usage: squish-pty COMMAND [ARG]...\n"
+               "Squishes both stdin and stdout into a single pseudoterminal,\n"
+               "which is passed as stdout to run the specified COMMAND.\n");
+      return EXIT_FAILURE;
+    }
+
+  /* Open master side of pty and get ready to open slave. */
+  master = open ("/dev/ptmx", O_RDWR | O_NOCTTY);
+  if (master < 0)
+    fail_io ("open \"/dev/ptmx\"");
+  if (grantpt (master) < 0)
+    fail_io ("grantpt");
+  if (unlockpt (master) < 0)
+    fail_io ("unlockpt");
+
+  /* Open slave side of pty. */
+  name = ptsname (master);
+  if (name == NULL)
+    fail_io ("ptsname");
+  slave = open (name, O_RDWR);
+  if (slave < 0)
+    fail_io ("open \"%s\"", name);
+
+  /* System V implementations need STREAMS configuration for the
+     slave. */
+  if (isastream (slave))
+    {
+      if (ioctl (slave, I_PUSH, "ptem") < 0
+          || ioctl (slave, I_PUSH, "ldterm") < 0)
+        fail_io ("ioctl");
+    }
+
+  /* Arrange to get notified when a child dies, by writing a byte
+     to a pipe fd.  We really want to use pselect() and
+     sigprocmask(), but Solaris 2.7 doesn't have it. */
+  if (pipe (pipe_fds) < 0)
+    fail_io ("pipe");
+  dead_child_fd = pipe_fds[1];
+
+  memset (&sa, 0, sizeof sa);
+  sa.sa_handler = sigchld_handler;
+  sigemptyset (&sa.sa_mask);
+  sa.sa_flags = SA_RESTART;
+  if (sigaction (SIGCHLD, &sa, NULL) < 0)
+    fail_io ("sigaction");
+
+  /* Save the virtual interval timer, which might have been set
+     by the process that ran us.  It really should be applied to
+     our child process. */
+  memset (&zero_itimerval, 0, sizeof zero_itimerval);
+  if (setitimer (ITIMER_VIRTUAL, &zero_itimerval, &old_itimerval) < 0)
+    fail_io ("setitimer");
+  
+  pid = fork ();
+  if (pid < 0)
+    fail_io ("fork");
+  else if (pid != 0) 
+    {
+      /* Running in parent process. */
+      int status;
+      close (slave);
+      relay (master, pipe_fds[0]);
+
+      /* If the subprocess has died, die in the same fashion.
+         In particular, dying from SIGVTALRM tells the pintos
+         script that we ran out of CPU time. */
+      if (waitpid (pid, &status, WNOHANG) > 0)
+        {
+          if (WIFEXITED (status))
+            return WEXITSTATUS (status);
+          else if (WIFSIGNALED (status))
+            raise (WTERMSIG (status));
+        }
+      return 0; 
+    }
+  else 
+    {
+      /* Running in child process. */
+      if (setitimer (ITIMER_VIRTUAL, &old_itimerval, NULL) < 0)
+        fail_io ("setitimer");
+      if (dup2 (slave, STDOUT_FILENO) < 0)
+        fail_io ("dup2");
+      if (close (pipe_fds[0]) < 0 || close (pipe_fds[1]) < 0
+          || close (slave) < 0 || close (master) < 0)
+        fail_io ("close");
+      execvp (argv[1], argv + 1);
+      fail_io ("exec");
+    }
+}
diff --git a/src/utils/squish-unix.c b/src/utils/squish-unix.c
new file mode 100644
index 0000000..805205b
--- /dev/null
+++ b/src/utils/squish-unix.c
@@ -0,0 +1,338 @@
+#define _GNU_SOURCE 1
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stropts.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <termios.h>
+#include <unistd.h>
+
+static void
+fail_io (const char *msg, ...)
+     __attribute__ ((noreturn))
+     __attribute__ ((format (printf, 1, 2)));
+
+/* Prints MSG, formatting as with printf(),
+   plus an error message based on errno,
+   and exits. */
+static void
+fail_io (const char *msg, ...)
+{
+  va_list args;
+
+  va_start (args, msg);
+  vfprintf (stderr, msg, args);
+  va_end (args);
+
+  if (errno != 0)
+    fprintf (stderr, ": %s", strerror (errno));
+  putc ('\n', stderr);
+  exit (EXIT_FAILURE);
+}
+
+/* If FD is a terminal, configures it for noncanonical input mode
+   with VMIN and VTIME set as indicated.
+   If FD is not a terminal, has no effect. */
+static void
+make_noncanon (int fd, int vmin, int vtime)
+{
+  if (isatty (fd)) 
+    {
+      struct termios termios;
+      if (tcgetattr (fd, &termios) < 0)
+        fail_io ("tcgetattr");
+      termios.c_lflag &= ~(ICANON | ECHO);
+      termios.c_cc[VMIN] = vmin;
+      termios.c_cc[VTIME] = vtime;
+      if (tcsetattr (fd, TCSANOW, &termios) < 0)
+        fail_io ("tcsetattr");
+    }
+}
+
+/* Make FD non-blocking if NONBLOCKING is true,
+   or blocking if NONBLOCKING is false. */
+static void
+make_nonblocking (int fd, bool nonblocking) 
+{
+  int flags = fcntl (fd, F_GETFL);
+  if (flags < 0)
+    fail_io ("fcntl");
+  if (nonblocking)
+    flags |= O_NONBLOCK;
+  else
+    flags &= ~O_NONBLOCK;
+  if (fcntl (fd, F_SETFL, flags) < 0)
+    fail_io ("fcntl");
+}
+
+/* Handle a read or write on *FD, which is the socket if
+   FD_IS_SOCK is true, that returned end-of-file or error
+   indication RETVAL.  The system call is named CALL, for use in
+   error messages.  Returns true if processing may continue,
+   false if we're all done. */
+static bool
+handle_error (ssize_t retval, int *fd, bool fd_is_sock, const char *call)
+{
+  if (retval == 0)
+    {
+      if (fd_is_sock)
+        return false;
+      else
+        {
+          *fd = -1;
+          return true;
+        }
+    }
+  else
+    fail_io (call); 
+}
+
+/* Copies data from stdin to SOCK and from SOCK to stdout until no
+   more data can be read or written. */
+static void
+relay (int sock) 
+{
+  struct pipe 
+    {
+      int in, out;
+      char buf[BUFSIZ];
+      size_t size, ofs;
+      bool active;
+    };
+  struct pipe pipes[2];
+
+  /* In case stdin is a file, go back to the beginning.
+     This allows replaying the input on reset. */
+  lseek (STDIN_FILENO, 0, SEEK_SET);
+
+  /* Make SOCK, stdin, and stdout non-blocking. */
+  make_nonblocking (sock, true);
+  make_nonblocking (STDIN_FILENO, true);
+  make_nonblocking (STDOUT_FILENO, true);
+
+  /* Configure noncanonical mode on stdin to avoid waiting for
+     end-of-line. */
+  make_noncanon (STDIN_FILENO, 1, 0);
+
+  memset (pipes, 0, sizeof pipes);
+  pipes[0].in = STDIN_FILENO;
+  pipes[0].out = sock;
+  pipes[1].in = sock;
+  pipes[1].out = STDOUT_FILENO;
+  
+  while (pipes[0].in != -1 || pipes[1].in != -1
+         || (pipes[1].size && pipes[1].out != -1))
+    {
+      fd_set read_fds, write_fds;
+      sigset_t empty_set;
+      int retval;
+      int i;
+
+      FD_ZERO (&read_fds);
+      FD_ZERO (&write_fds);
+      for (i = 0; i < 2; i++)
+        {
+          struct pipe *p = &pipes[i];
+
+          /* Don't do anything with the stdin->sock pipe until we
+             have some data for the sock->stdout pipe.  If we get
+             too eager, vmplayer will throw away our input. */
+          if (i == 0 && !pipes[1].active)
+            continue;
+          
+          if (p->in != -1 && p->size + p->ofs < sizeof p->buf)
+            FD_SET (p->in, &read_fds);
+          if (p->out != -1 && p->size > 0)
+            FD_SET (p->out, &write_fds); 
+        }
+      sigemptyset (&empty_set);
+      retval = pselect (FD_SETSIZE, &read_fds, &write_fds, NULL, NULL,
+                        &empty_set);
+      if (retval < 0) 
+        {
+          if (errno == EINTR)
+            {
+              /* Child died.  Do final relaying. */
+              struct pipe *p = &pipes[1];
+              if (p->out == -1)
+                exit (0);
+              make_nonblocking (STDOUT_FILENO, false);
+              for (;;) 
+                {
+                  ssize_t n;
+                  
+                  /* Write buffer. */
+                  while (p->size > 0) 
+                    {
+                      n = write (p->out, p->buf + p->ofs, p->size);
+                      if (n < 0)
+                        fail_io ("write");
+                      else if (n == 0)
+                        fail_io ("zero-length write");
+                      p->ofs += n;
+                      p->size -= n;
+                    }
+                  p->ofs = 0;
+
+                  p->size = n = read (p->in, p->buf, sizeof p->buf);
+                  if (n <= 0)
+                    exit (0);
+                }
+            }
+          fail_io ("select"); 
+        }
+
+      for (i = 0; i < 2; i++) 
+        {
+          struct pipe *p = &pipes[i];
+          if (p->in != -1 && FD_ISSET (p->in, &read_fds))
+            {
+              ssize_t n = read (p->in, p->buf + p->ofs + p->size,
+                                sizeof p->buf - p->ofs - p->size);
+              if (n > 0) 
+                {
+                  p->active = true;
+                  p->size += n;
+                  if (p->size == BUFSIZ && p->ofs != 0)
+                    {
+                      memmove (p->buf, p->buf + p->ofs, p->size);
+                      p->ofs = 0;
+                    }
+                }
+              else if (!handle_error (n, &p->in, p->in == sock, "read"))
+                return;
+            }
+          if (p->out != -1 && FD_ISSET (p->out, &write_fds)) 
+            {
+              ssize_t n = write (p->out, p->buf + p->ofs, p->size);
+              if (n > 0) 
+                {
+                  p->ofs += n;
+                  p->size -= n;
+                  if (p->size == 0)
+                    p->ofs = 0;
+                }
+              else if (!handle_error (n, &p->out, p->out == sock, "write"))
+                return;
+            }
+        }
+    }
+}
+
+static void
+sigchld_handler (int signo __attribute__ ((unused))) 
+{
+  /* Nothing to do. */
+}
+
+int
+main (int argc __attribute__ ((unused)), char *argv[])
+{
+  pid_t pid;
+  struct itimerval zero_itimerval;
+  struct sockaddr_un sun;
+  sigset_t sigchld_set;
+  int sock;
+  
+  if (argc < 3) 
+    {
+      fprintf (stderr,
+               "usage: squish-unix SOCKET COMMAND [ARG]...\n"
+               "Squishes both stdin and stdout into a single Unix domain\n"
+               "socket named SOCKET, and runs COMMAND as a subprocess.\n");
+      return EXIT_FAILURE;
+    }
+
+  /* Create socket. */
+  sock = socket (PF_LOCAL, SOCK_STREAM, 0);
+  if (sock < 0)
+    fail_io ("socket");
+
+  /* Configure socket. */
+  sun.sun_family = AF_LOCAL;
+  strncpy (sun.sun_path, argv[1], sizeof sun.sun_path);
+  sun.sun_path[sizeof sun.sun_path - 1] = '\0';
+  if (unlink (sun.sun_path) < 0 && errno != ENOENT)
+    fail_io ("unlink");
+  if (bind (sock, (struct sockaddr *) &sun,
+            (offsetof (struct sockaddr_un, sun_path)
+             + strlen (sun.sun_path) + 1)) < 0)
+    fail_io ("bind");
+
+  /* Listen on socket. */
+  if (listen (sock, 1) < 0)
+    fail_io ("listen");
+
+  /* Block SIGCHLD and set up a handler for it. */
+  sigemptyset (&sigchld_set);
+  sigaddset (&sigchld_set, SIGCHLD);
+  if (sigprocmask (SIG_BLOCK, &sigchld_set, NULL) < 0)
+    fail_io ("sigprocmask");
+  if (signal (SIGCHLD, sigchld_handler) == SIG_ERR)
+    fail_io ("signal");
+
+  /* Save the virtual interval timer, which might have been set
+     by the process that ran us.  It really should be applied to
+     our child process. */
+  memset (&zero_itimerval, 0, sizeof zero_itimerval);
+  if (setitimer (ITIMER_VIRTUAL, &zero_itimerval, NULL) < 0)
+    fail_io ("setitimer");
+  
+  pid = fork ();
+  if (pid < 0)
+    fail_io ("fork");
+  else if (pid != 0) 
+    {
+      /* Running in parent process. */
+      make_nonblocking (sock, true);
+      for (;;) 
+        {
+          fd_set read_fds;
+          sigset_t empty_set;
+          int retval;
+          int conn;
+
+          /* Wait for connection. */
+          FD_ZERO (&read_fds);
+          FD_SET (sock, &read_fds);
+          sigemptyset (&empty_set);
+          retval = pselect (sock + 1, &read_fds, NULL, NULL, NULL, &empty_set);
+          if (retval < 0) 
+            {
+              if (errno == EINTR)
+                break;
+              fail_io ("select"); 
+            }
+
+          /* Accept connection. */
+          conn = accept (sock, NULL, NULL);
+          if (conn < 0)
+            fail_io ("accept");
+
+          /* Relay connection. */
+          relay (conn);
+          close (conn);
+        }
+      return 0; 
+    }
+  else 
+    {
+      /* Running in child process. */
+      if (close (sock) < 0)
+        fail_io ("close");
+      execvp (argv[2], argv + 2);
+      fail_io ("exec");
+    }
+}
diff --git a/src/vm/.gitignore b/src/vm/.gitignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/src/vm/.gitignore
@@ -0,0 +1,3 @@
+build
+bochsrc.txt
+bochsout.txt
diff --git a/src/vm/Make.vars b/src/vm/Make.vars
new file mode 100644
index 0000000..e3c33a7
--- /dev/null
+++ b/src/vm/Make.vars
@@ -0,0 +1,7 @@
+# -*- makefile -*-
+
+kernel.bin: DEFINES = -DUSERPROG -DFILESYS -DVM
+KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys vm
+TEST_SUBDIRS = tests/userprog tests/vm tests/filesys/base
+GRADING_FILE = $(SRCDIR)/tests/vm/Grading
+SIMULATOR = --qemu
diff --git a/src/vm/Makefile b/src/vm/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/src/vm/Makefile
@@ -0,0 +1 @@
+include ../Makefile.kernel
diff --git a/src/vm/frame.c b/src/vm/frame.c
new file mode 100644
index 0000000..b94089d
--- /dev/null
+++ b/src/vm/frame.c
@@ -0,0 +1,346 @@
+/* ----- Newly Add for Proj03 VM ----- */
+
+#include <devices/block.h>
+#include <threads/vaddr.h>
+#include <stdio.h>
+#include <userprog/syscall.h>
+#include "string.h"
+#include "frame.h"
+#include "swap.h"
+#include "../threads/thread.h"
+#include "../userprog/pagedir.h"
+#include "../threads/malloc.h"
+#include "../lib/debug.h"
+#include "../lib/stddef.h"
+#include "../lib/kernel/hash.h"
+#include "page.h"
+
+
+static struct hash frame_table;            /* Hash table for managing physical frame entries. */
+static struct list frame_clock_list;       /* List for implementing the clock page replacement algorithm. */
+static struct lock all_lock;               /* Lock to ensure thread synchronization across frame management. */
+struct frame_item* current_frame;          /* Pointer to the current frame being used or managed. */
+
+
+void* frame_get_used_frame(void *upage);
+void frame_current_clock_to_next();
+void frame_current_clock_to_prev();
+
+
+static bool frame_hash_less(const struct hash_elem *a,
+                     const struct hash_elem *b,
+                     void *aux UNUSED);
+static unsigned frame_hash(const struct hash_elem *e,
+                    void* aux UNUSED);
+
+
+/* Initialize the frame management system. */
+void 
+frame_init() 
+{
+  /* Initialize the hash table for frame management. */
+  hash_init(&frame_table, frame_hash, frame_hash_less, NULL);
+
+  /* Initialize the clock list used for page replacement. */
+  list_init(&frame_clock_list);
+
+  /* Initialize the lock for synchronizing frame access. */
+  lock_init(&all_lock);
+
+  /* Set the current frame pointer to NULL. */
+  current_frame = NULL;
+}
+
+
+/* Allocate a frame for the given user page (upage) and return the frame's address.
+   If no idle frame is available, it will use `frame_get_used_frame` to retrieve one.
+   If the PAL_ZERO flag is set, the frame will be zeroed. */
+void *
+frame_get_frame (enum palloc_flags flag, void *upage)
+{
+  lock_acquire (&all_lock);
+
+  /* Ensure the address is page-aligned and valid. */
+  ASSERT (pg_ofs (upage) == 0);
+  ASSERT (is_user_vaddr (upage));
+
+  /* Attempt to allocate a frame for the page. */
+  void *frame = palloc_get_page (flag);
+
+  /* If no frame is available, retrieve a used frame and zero it if needed. */
+  if (frame == NULL)
+  {
+    frame = frame_get_used_frame (upage);
+    if (flag & PAL_ZERO)
+    {
+      memset(frame, 0, PGSIZE);
+    }
+  }
+
+  /* If no frame could be allocated. */
+  if (frame == NULL)
+  {
+    lock_release(&all_lock);
+    return NULL;
+  }
+
+  /* Ensure the allocated frame is page-aligned. */
+  ASSERT(pg_ofs(frame) == 0);
+
+  /* Create a new frame item and insert it into the frame table. */
+  struct frame_item *tmp = (struct frame_item *)malloc (sizeof (struct frame_item));
+  tmp->frame = frame;
+  tmp->upage = upage;
+  tmp->t = thread_current ();
+  tmp->pinned = true;
+  hash_insert (&frame_table, &tmp->hash_elem);
+
+  lock_release(&all_lock);
+
+  /* Return the allocated frame. */
+  return frame;
+}
+
+/* Releases a physical frame no longer in use and cleans up
+   its associated metadata. */
+void 
+frame_free_frame (void *frame)
+{
+  lock_acquire (&all_lock);
+
+  struct frame_item *t = frame_lookup (frame);
+
+  /* If the frame is not pinned, proceed with freeing it. */
+  if (!t->pinned)
+  {
+    /* If the frame is currently being clocked, update the clock. */
+    if (current_frame == t)
+    {
+      if (list_empty (&frame_clock_list))
+      {
+        current_frame = NULL;
+      }
+      else
+      {
+        frame_current_clock_to_next ();
+      }
+    }
+    list_remove (&t->list_elem);
+  }
+
+  /* Remove the frame item from hash table, free its memory. */
+  hash_delete (&frame_table, &t->hash_elem);
+  free (t);
+
+  /* Free the allocated page for the frame. */
+  palloc_free_page (frame);
+
+  lock_release (&all_lock);
+}
+
+
+/* Checks if the given frame is pinned
+   (i.e., cannot be swapped out). */
+bool 
+frame_get_pinned (void *frame)
+{
+  struct frame_item *t = frame_lookup (frame);
+  return t->pinned;
+}
+
+/* Sets the pinned status of the given frame to false.
+   If the frame was pinned, it is added to the frame clock list. */
+bool 
+frame_set_pinned_false (void *frame)
+{
+  lock_acquire (&all_lock);
+
+  struct frame_item *t = frame_lookup(frame);
+
+  /* If the frame is not found, return false. */
+  if (t == NULL)
+  {
+    lock_release(&all_lock);
+    return false;
+  }
+
+  /* If the frame is already unpinned, return true. */
+  if (t->pinned == false)
+  {
+    lock_release(&all_lock);
+    return true;
+  }
+
+  /* Set the frame as unpinned and add it to the frame clock list. */
+  t->pinned = false;
+  list_push_back (&frame_clock_list, &t->list_elem);
+
+  /* If the first frame in the clock list, set it as the current frame. */
+  if (list_size (&frame_clock_list) == 1)
+  {
+    current_frame = t;
+  }
+
+  lock_release (&all_lock);
+  return true;
+}
+
+
+/* Locates an occupied frame (current_frame) and determines if it can
+   be replaced based on the page access information. If the frame needs 
+   to be replaced, saves its contents to the swap space or a mapped file.
+   Removes the frame from the frame table and frees its physical memory 
+   for use by a new page. */
+void* 
+frame_get_used_frame(void *upage)
+{
+  /* Iterate until a frame that has not been accessed is found. */
+  while (pagedir_is_accessed (current_frame->t->pagedir, current_frame->upage))
+  {
+    pagedir_set_accessed (current_frame->t->pagedir, current_frame->upage, false);
+    frame_current_clock_to_next ();
+  }
+
+  /* Current frame is chosen for eviction. */
+  struct frame_item *t = current_frame;
+  void *tmp_frame = t->frame;
+  index_t index = (index_t)-1;
+
+  /* Ensure the page table entry exists for the current frame's page. */
+  ASSERT (page_find (current_frame->t->page_table, current_frame->upage) != NULL);
+
+  /* Retrieve the page table entry for the current frame's page. */
+  struct page_table_elem *pte = page_find (
+                                current_frame->t->page_table, current_frame->upage); 
+
+  /* Determine the eviction strategy based on the page's origin. */
+  if (pte == NULL || pte->origin == NULL || 
+      ((struct mmap_file *)(pte->origin))->is_static_data)
+  {
+    /* Store the frame's contents to the swap space. */
+    index = swap_store (current_frame->frame);
+
+    /* If swap storage failed. */
+    if (index == -1)
+    {
+      return NULL;
+    }
+
+    /* Evict the page by updating its status to SWAP. */
+    ASSERT (page_status_eviction(current_frame->t, 
+                                 current_frame->upage, index, true));
+  }
+  else
+  {
+    /* Write the frame's contents back to the mapped file. */
+    mmap_write_file(pte->origin, current_frame->upage, tmp_frame);
+
+    /* Evict the page by updating its status to FILE. */
+    ASSERT (page_status_eviction (current_frame->t, 
+                                  current_frame->upage, index, false));
+  }
+
+  /* Remove the frame from the clock list. */
+  list_remove (&t->list_elem);
+
+  /* Update the current_frame pointer. */
+  if (list_empty (&frame_clock_list))
+  {
+    current_frame = NULL;
+  }
+  else
+  {
+    frame_current_clock_to_next ();
+  }
+
+  /* Remove the frame from the frame table and free its metadata. */
+  hash_delete (&frame_table, &t->hash_elem);
+  free (t);
+
+  /* Return the freed frame for reuse. */
+  return tmp_frame;
+}
+
+
+/* Advance the current_frame pointer to the next frame in the frame_clock_list.
+   This function follows the Clock algorithm for page replacement. If the list
+   contains only one frame, no advancement occurs. Otherwise, it moves to the
+   next frame in the list, wrapping around to the head if necessary. */
+void 
+frame_current_clock_to_next()
+{
+  ASSERT (current_frame != NULL);
+
+  /* If there's only one frame in the clock list, there's no need to advance. */
+  if (list_size(&frame_clock_list) == 1)
+  {
+    return;
+  }
+
+  /* If the current frame is the last one in the list, wrap around to the first. */
+  if (&current_frame->list_elem == list_back (&frame_clock_list))
+  {
+    current_frame = list_entry (list_head (&frame_clock_list),
+                                struct frame_item, list_elem);
+  }
+  /* Move to the next frame in the list. */
+  current_frame = list_entry (list_next (&current_frame->list_elem),
+                              struct frame_item, list_elem);
+}
+
+/* Move the current_frame pointer to the previous frame in the frame_clock_list.
+   This function follows the Clock algorithm for page replacement, but in reverse.
+   If the list contains only one frame, no advancement occurs. Otherwise, it moves 
+   to the previous frame in the list, wrapping around to the last frame if necessary. */
+void 
+frame_current_clock_to_prev ()
+{
+  ASSERT (current_frame != NULL);
+
+  /* If there's only one frame in the clock list, there's no need to move. */
+  if (list_size (&frame_clock_list) == 1)
+    return;
+
+  /* If the current frame is the first in the list, wrap around to the last frame. */
+  if (&current_frame->list_elem == list_front (&frame_clock_list))
+  {
+    current_frame = list_entry (list_tail (&frame_clock_list),
+                                struct frame_item, list_elem);
+  }
+  /* Move to the previous frame in the list. */
+  current_frame = list_entry (list_prev (&current_frame->list_elem),
+                              struct frame_item, list_elem);
+}
+
+/* Search for a frame in the global frame table hash, returns the associated
+   frame item if found, or NULL if the frame is not found. */
+void *frame_lookup (void *frame)
+{
+  struct frame_item p;
+  struct hash_elem *e;
+
+  p.frame = frame;
+  e = hash_find (&frame_table, &p.hash_elem);
+
+  return e == NULL ? NULL : hash_entry (e, struct frame_item, hash_elem);
+}
+
+
+/* Compare two frame items based on their frame addresses for use in a hash table. 
+   Return true if the frame address of 'a' is less than that of 'b'. */
+static bool frame_hash_less(const struct hash_elem *a, 
+                            const struct hash_elem *b, void *aux UNUSED)
+{
+  const struct frame_item *ta = hash_entry (a, struct frame_item, hash_elem);
+  const struct frame_item *tb = hash_entry (b, struct frame_item, hash_elem);
+  return (ta->frame < tb->frame);
+}
+
+/* Returns the hash value used for storing the frame item in the hash table. */
+static unsigned frame_hash (const struct hash_elem *e, void* aux UNUSED)
+{
+  struct frame_item* t = hash_entry (e, struct frame_item, hash_elem);
+  return hash_bytes (&t->frame, sizeof(t->frame));
+}
+
+/* ----- Newly Add for Proj03 VM ----- */
\ No newline at end of file
diff --git a/src/vm/frame.h b/src/vm/frame.h
new file mode 100644
index 0000000..c13f19d
--- /dev/null
+++ b/src/vm/frame.h
@@ -0,0 +1,42 @@
+/* ----- Newly Add for Proj03 VM ----- */
+#ifndef MYPINTOS_FRAME_H
+#define MYPINTOS_FRAME_H
+
+#include "../lib/stdbool.h"
+#include "../threads/palloc.h"
+
+struct frame_item {
+    void *frame;                     /* Pointer to the physical frame in memory. */
+    void *upage;                     /* Pointer to the user virtual address associated with the frame. */
+    struct thread* t;                /* Pointer to the thread that owns the frame. */
+    bool pinned;                     /* Indicates if the frame is pinned (cannot be evicted). */
+    struct hash_elem hash_elem;      /* Element used for insertion in a hash table (for frame management). */
+    struct list_elem list_elem;      /* Element used for insertion in a list (for managing frames in a list). */
+};
+
+void *frame_lookup(void *frame);
+
+//init frame_table
+//used in thread/init.c
+void frame_init();
+
+//get a frame from user pool, which must be mapped from upage
+//in other words, in page_table, upage->frame_get_frame(flag, upage)
+//flag is used by palloc_get_page
+void* frame_get_frame(enum palloc_flags flag, void *upage);
+
+//free a frame that got from frame_get_frame
+void  frame_free_frame(void *frame);
+
+//get pinned accord to frame
+//if a page is pinned, it won't be swaped to the disk
+bool  frame_get_pinned(void* frame);
+
+// set frame pinned to new_value
+// return whether the set_pinned success
+bool frame_set_pinned_false(void* frame);
+
+
+#endif //MYPINTOS_FRAME_H
+
+/* ----- Newly Add for Proj03 VM ----- */
diff --git a/src/vm/page.c b/src/vm/page.c
new file mode 100644
index 0000000..fcf6746
--- /dev/null
+++ b/src/vm/page.c
@@ -0,0 +1,507 @@
+/* ----- Newly Add for Proj03 VM ----- */
+
+#include <stdio.h>
+#include "page.h"
+#include "frame.h"
+#include "swap.h"
+#include "../threads/thread.h"
+#include "../userprog/pagedir.h"
+#include "../userprog/syscall.h"
+#include "../lib/stddef.h"
+#include "../threads/malloc.h"
+#include "../lib/debug.h"
+#include "../threads/vaddr.h"
+
+#define PAGE_PAL_FLAG			0
+#define PAGE_INST_MARGIN		32
+#define PAGE_STACK_SIZE			0x800000
+#define PAGE_STACK_UNDERLINE	(PHYS_BASE - PAGE_STACK_SIZE)
+
+bool page_hash_less(const struct hash_elem *lhs,
+					 const struct hash_elem *rhs,
+					 void *aux UNUSED);
+					 
+unsigned page_hash(const struct hash_elem *e,
+					void *aux UNUSED);
+					
+void page_destroy_frame_likes(struct hash_elem *e,
+							void *aux UNUSED);
+
+static struct lock page_lock;
+
+void page_lock_init() 
+{
+	lock_init(&page_lock);
+}
+
+/* Allocates memory for a new page table and initializes it.
+   If allocation or initialization fails, NULL is returned.
+	 Otherwise, the newly created and initialized page table is returned. */
+page_table_t* 
+page_create() 
+{
+  page_table_t *page_table = malloc (sizeof (page_table_t));
+
+  if (page_table != NULL) 
+  {
+    if (page_init (page_table) == false) 
+    {
+      free (page_table);
+      return NULL;
+    }
+    else 
+    {
+      return page_table;
+    }
+  }
+  else 
+  {
+    return NULL;
+  }
+}
+
+/* Initializes a page table by creating the initial virtual stack slot
+	 and setting up the hash table for the page table entries.
+	 Returns true if the initialization is successful, false otherwise. */
+bool 
+page_init (page_table_t *page_table) 
+{
+  return hash_init (page_table, page_hash, page_hash_less, NULL);
+}
+
+
+/* Destroys the page table and recycles associated resources such as 
+	 FRAME and SWAP slots. */
+void 
+page_destroy (page_table_t *page_table) 
+{
+  /* Acquire lock to ensure thread-safe destruction. */
+  lock_acquire (&page_lock);
+  hash_destroy (page_table, page_destroy_frame_likes);
+  lock_release (&page_lock);
+}
+
+
+/* Finds the element in the page table with the specified virtual address (upage).
+	 Returns a pointer to the page table entry if found, or NULL if not found. */
+struct page_table_elem* 
+page_find (page_table_t *page_table, void *upage) 
+{
+  struct hash_elem *e;
+  struct page_table_elem tmp;
+
+  ASSERT (page_table != NULL);
+
+  tmp.key = upage;
+
+  e = hash_find (page_table, &(tmp.elem));
+
+  if (e != NULL) 
+  {
+    return hash_entry(e, struct page_table_elem, elem);
+  }
+  else 
+  {
+    return NULL;
+  }
+}
+
+
+/* Page fault handler for the page table.
+   Given a virtual address (vaddr), this function handles page faults by 
+   allocating a frame for the page and handling different scenarios:
+   stack growth, SWAP, and memory-mapped files.
+   Returns whether the page fault was successfully handled or not. */
+bool 
+page_fault_handler (const void *vaddr, bool to_write, void *esp) 
+{
+  struct thread *cur = thread_current ();
+  page_table_t *page_table = cur->page_table;
+  uint32_t *pagedir = cur->pagedir;
+  void *upage = pg_round_down (vaddr);
+	void *dest = NULL;
+  bool success = true;
+
+  lock_acquire (&page_lock);
+
+  /* Find the page table entry for the given virtual address. */
+  struct page_table_elem *pte = page_find (page_table, upage);
+
+  /* Ensure the virtual address is valid and not already mapped to a frame. */
+  ASSERT(is_user_vaddr(vaddr));
+  ASSERT(!(pte != NULL && pte->status == FRAME));
+
+  /* Handle illegal write attempt. */
+  if (to_write && pte != NULL && !pte->writable) {
+    return false;
+  }
+
+  /* Handle stack growth for addresses above the stack underline. */
+  if (upage >= PAGE_STACK_UNDERLINE)
+	{
+    if (vaddr >= esp - PAGE_INST_MARGIN)
+		{ 
+      /* If the page is not in page table, allocate a new frame. */
+      if (pte == NULL)
+			{
+        dest = frame_get_frame (PAGE_PAL_FLAG, upage);
+        if (dest == NULL)
+				{
+          success = false;
+        }
+				else 
+				{
+          pte = malloc (sizeof (*pte));
+          pte->key = upage;
+          pte->value = dest;
+          pte->status = FRAME;
+          pte->writable = true;
+          pte->origin = NULL;
+          hash_insert (page_table, &pte->elem);
+        }
+      }
+			/* If the page is in page table. */
+			else 
+			{
+				/* If the page is in swap, load it into a frame. */
+        if (pte->status == SWAP)
+				{
+          dest = frame_get_frame (PAGE_PAL_FLAG, upage);
+          if (dest == NULL)
+					{
+            success = false;
+          }
+					else
+					{
+            swap_load ((index_t)pte->value, dest);
+            pte->value = dest;
+            pte->status = FRAME;
+          }
+        }
+				/* Invalid page status. */
+				else
+				{
+          success = false;
+        }
+      }
+    }
+		/* Invalid address for stack growth. */
+		else
+		{
+      success = false;
+    }
+  }
+	/* Handle non-stack memory access. */
+	else
+	{
+		/* Page not found. */
+    if (pte == NULL)
+		{
+      success = false;
+    }
+		else
+		{
+      /* Handle page status: SWAP. */
+      if (pte->status == SWAP)
+			{
+        dest = frame_get_frame (PAGE_PAL_FLAG, upage);
+        if (dest == NULL)
+				{
+          success = false;
+        }
+				else
+				{
+          swap_load ((index_t)pte->value, dest);
+          pte->value = dest;
+          pte->status = FRAME;
+        }
+      }
+      /* Handle page status: FILE. */
+			else if (pte->status == FILE)
+			{
+        dest = frame_get_frame (PAGE_PAL_FLAG, upage);
+        if (dest == NULL) {
+          success = false;
+        } else {
+          mmap_read_file (pte->value, upage, dest);
+          pte->value = dest;
+          pte->status = FRAME;
+        }
+      }
+			/* Invalid page status. */
+			else
+			{
+        success = false;
+      }
+    }
+  }
+
+  /* Unpin the frame and release the lock. */
+  frame_set_pinned_false (dest);
+  lock_release (&page_lock);
+
+  /* If the page fault was handled successfully, update the page directory. */
+  if (success)
+	{
+    ASSERT (pagedir_set_page(pagedir, pte->key, pte->value, pte->writable));
+  }
+
+  return success;
+}
+
+/* Verifies that there isn't already a page at the specified virtual address,
+	 then maps the provided physical page to that virtual address in the page table. */
+bool 
+page_set_frame (void *upage, void *kpage, bool is_writable) 
+{
+  struct thread *cur = thread_current ();
+  page_table_t *page_table = cur->page_table;
+  uint32_t *pagedir = cur->pagedir;
+
+  bool success = true;
+  lock_acquire (&page_lock);
+
+  /* Check if the page is already mapped. */
+  struct page_table_elem *pte = page_find (page_table, upage);
+  if (pte == NULL)
+	{
+    /* Page is not mapped, create a new entry for it. */
+    pte = malloc (sizeof (*pte));
+    pte->key = upage;
+    pte->value = kpage;
+    pte->status = FRAME;
+    pte->origin = NULL;
+    pte->writable = is_writable;
+
+    /* Insert the new page table entry. */
+    hash_insert (page_table, &pte->elem);
+  }
+	else
+	{
+    /* Page is already mapped. */
+    success = false;
+  }
+
+  lock_release (&page_lock);
+
+  /* If successful, set the page in the page directory. */
+  if (success)
+	{
+    ASSERT (pagedir_set_page (pagedir, pte->key, pte->value, pte->writable));
+  }
+
+  return success;
+}
+
+
+/* Check if upage is below stack underline and available*/
+bool page_available_upage (page_table_t *page_table, void *upage) 
+{
+	return upage < PAGE_STACK_UNDERLINE && page_find (page_table, upage) == NULL;
+}
+
+/* Check if upage is accessible for a non-stack visit */
+bool page_accessible_upage (page_table_t *page_table, void *upage) 
+{
+	return upage < PAGE_STACK_UNDERLINE 
+					&& page_find (page_table, upage) != NULL;
+}
+
+/* Installs a file mapping into the page table.
+   The function checks if the page is available for the given virtual address,
+   then creates a page table entry for the file. */
+bool 
+page_install_file (page_table_t *page_table, struct mmap_file *mf, void *key) 
+{
+  struct thread *cur = thread_current ();
+  bool success = true;
+
+  lock_acquire (&page_lock);
+
+  /* Check if the page is available for mapping. */
+  if (page_available_upage (page_table, key))
+	{
+    /* Create a new page table entry for the file mapping. */
+    struct page_table_elem *pte = malloc (sizeof (*pte));
+    pte->key = key;
+    pte->value = mf;
+    pte->status = FILE;
+    pte->writable = mf->writable;
+    pte->origin = mf;
+
+    /* Insert the new page table entry into the hash table. */
+    hash_insert (page_table, &pte->elem);
+  }
+	else
+	{
+    success = false;
+  }
+
+  lock_release (&page_lock);
+
+  return success;
+}
+
+
+
+/* Unmounts a file or frees a frame in the page table.
+   Depending on the status of the page, the function either deletes the file mapping 
+   or writes back to the file and frees the frame. */
+bool 
+page_unmap (page_table_t *page_table, void *upage) 
+{
+  struct thread *cur = thread_current();
+  bool success = true;
+
+  lock_acquire(&page_lock);
+
+  /* Check if the page is accessible in the page table. */
+  if (page_accessible_upage(page_table, upage))
+	{
+    struct page_table_elem *pte = page_find(page_table, upage);
+
+    ASSERT(pte != NULL);
+
+    /* Handle different statuses of the page. */
+    switch (pte->status)
+		{
+      case FILE:
+        /* If the page is a file, just delete the entry. */
+        hash_delete(page_table, &(pte->elem));
+        free(pte);
+        break;
+
+      case FRAME:
+        /* If the page is a frame, write back to the file if dirty,
+					 then free the frame. */
+        if (pagedir_is_dirty (cur->pagedir, pte->key))
+				{
+          mmap_write_file (pte->origin, pte->key, pte->value);
+        }
+        pagedir_clear_page (cur->pagedir, pte->key);
+        hash_delete (page_table, &(pte->elem));
+        frame_free_frame (pte->value);
+        free (pte);
+        break;
+
+      default:
+        /* If the status is unknown, set success to false. */
+        success = false;
+    }
+
+  } else {
+    /* If the page is not accessible, set success to false. */
+    success = false;
+  }
+
+  lock_release(&page_lock);
+  
+  return success;
+}
+
+
+/* Switches a page from FRAME to SWAP or FILE based on the `to_swap` flag.
+   If the page is currently in the FRAME status, it is moved to either SWAP or FILE 
+   and the page directory entry is cleared. */
+bool 
+page_status_eviction (struct thread *cur, void *upage, void *index, bool to_swap) 
+{
+  struct page_table_elem *pte = page_find (cur->page_table, upage);
+  bool success = true;
+
+  /* Check if the page table entry exists and is in FRAME status. */
+  if (pte != NULL && pte->status == FRAME)
+	{
+    /* If moving the page to swap. */
+    if (to_swap)
+		{
+      pte->value = index;
+      pte->status = SWAP;
+    } 
+    /* Otherwise, move the page to FILE. */
+    else
+		{
+      ASSERT (pte->origin != NULL);
+      pte->value = pte->origin;
+      pte->status = FILE;
+    }
+    
+    /* Clear the page from the page directory. */
+    pagedir_clear_page(cur->pagedir, upage);
+  } 
+  else
+	{
+    /* If the page does not exist or is not in FRAME status, log the error. */
+    if (pte != NULL)
+		{
+      printf ("%s\n", pte->status == FILE ? "file" : "swap");
+    } 
+    else
+		{
+      puts ("NULL");
+    }
+    success = false;
+  }
+
+  return success;
+}
+
+/* Returns true if the virtual address of the first page is less than the second. */
+bool 
+page_hash_less (const struct hash_elem *lhs, const struct hash_elem *rhs, void *aux UNUSED) 
+{
+  /* Compare the virtual addresses (keys) of the two page table elements. */
+  return hash_entry(lhs, struct page_table_elem, elem)->key < 
+         hash_entry(rhs, struct page_table_elem, elem)->key;
+}
+
+
+/* Compute the hash value for elements in the page hash table. */
+unsigned 
+page_hash (const struct hash_elem *e, void *aux UNUSED)
+{
+  /* Retrieve the page table element from the hash entry. */
+  struct page_table_elem *pte = hash_entry (e, struct page_table_elem, elem);
+
+  /* Return the hash of the page's virtual address (key). */
+  return hash_bytes (&(pte->key), sizeof (pte->key));
+}
+
+
+/*  Frees the resources associated with a page table element,
+   returning FRAME and SWAP slots back to the system. */
+void 
+page_destroy_frame_likes(struct hash_elem *e, void *aux UNUSED) 
+{
+  struct page_table_elem *pte = hash_entry(e, struct page_table_elem, elem);
+
+  /* If the page status is FRAME, clear the page and free the frame. */
+  if (pte->status == FRAME) 
+  {
+    pagedir_clear_page (thread_current()->pagedir, pte->key);
+    frame_free_frame (pte->value);
+  } 
+  /* If the page status is SWAP, free the swap slot. */
+  else if (pte->status == SWAP) 
+  {
+    swap_free ((index_t) pte->value);
+  }
+
+  free (pte);
+}
+
+/* Finds the page table element with the page lock. */
+struct page_table_elem* 
+page_find_with_lock (page_table_t *page_table, void *upage) 
+{
+  lock_acquire (&page_lock);
+  
+  struct page_table_elem* tmp = page_find (page_table, upage);
+  
+  lock_release (&page_lock);
+  
+  return tmp;
+}
+
+
+/* ----- Newly Add for Proj03 VM ----- */
\ No newline at end of file
diff --git a/src/vm/page.h b/src/vm/page.h
new file mode 100644
index 0000000..1a5f082
--- /dev/null
+++ b/src/vm/page.h
@@ -0,0 +1,56 @@
+/* ----- Newly Add for Proj03 VM ----- */
+
+#ifndef SUPPLEMENTAL_PAGE_TABLE_MODULE
+#define SUPPLEMENTAL_PAGE_TABLE_MODULE
+
+#include "../lib/stdint.h"
+#include "../lib/kernel/hash.h"
+#include "../threads/palloc.h"
+#include "../threads/thread.h"
+typedef struct hash page_table_t;
+
+/* Classify elements in page_table_elem */
+enum page_type {
+	FRAME,
+	SWAP,
+	FILE
+};
+
+/*
+	key		:	virtual address of page
+	value	:	physical address of frame	  status = FRAME
+						or index of SWAP slot			status = SWAP
+						or mapid of mapped file		status = FILE
+*/
+struct page_table_elem 
+{ 
+    void *key;                      /* Virtual address for this entry. */
+    void *value;                    /* Mapped physical frame or resource. */
+    void *origin;                   /* Original source of the page (e.g., file, swap). */
+    enum page_type status;          /* Page status/type (e.g., RAM, swap, file). */
+    bool writable;                  /* Whether the page is writable (true/false). */
+    struct hash_elem elem;          /* Hash table element for efficient lookup. */
+};
+
+void page_lock_init();
+page_table_t *page_create();
+bool page_init(page_table_t *page_table);
+void page_destroy(page_table_t *page_table);
+struct page_table_elem* page_find(page_table_t *page_table, void *upage);
+struct page_table_elem* page_find_with_lock(page_table_t *page_table, void *upage);
+
+/* page fault handler */
+bool
+page_fault_handler (const void *vaddr, bool to_write, void *esp);
+
+/* interfaces for other modules */
+bool page_set_frame (void *upage, void *kpage, bool wb);
+bool page_available_upage (page_table_t *page_table, void *upage);
+bool page_install_file (page_table_t *page_table, struct mmap_file *mh, void *key);
+bool page_status_eviction (struct thread *cur, void *upage, void *index, bool to_swap);
+bool page_unmap (page_table_t *page_table, void *upage);
+
+
+#endif
+
+/* ----- Newly Add for Proj03 VM ----- */
diff --git a/src/vm/swap.c b/src/vm/swap.c
new file mode 100644
index 0000000..d245ab0
--- /dev/null
+++ b/src/vm/swap.c
@@ -0,0 +1,142 @@
+/* ----- Newly Add for Proj03 VM ----- */
+
+#include <lib/debug.h>
+#include <threads/pte.h>
+#include <threads/malloc.h>
+#include "swap.h"
+#include "../lib/kernel/hash.h"
+
+const int BLOCK_PER_PAGE = PGSIZE / BLOCK_SECTOR_SIZE;
+
+
+struct swap_item {
+    index_t index;
+    struct list_elem list_elem;
+};
+
+static struct list swap_free_list;
+struct block* swap_block;
+index_t top_index = 0;
+
+index_t get_free_swap_slot ();
+
+/* Initialize the swap space. */
+void 
+swap_init() 
+{
+  /* Retrieve the block used for swap space. */
+  swap_block = block_get_role(BLOCK_SWAP);
+  ASSERT(swap_block != NULL);
+
+  /* Initialize the free list for swap slots. */
+  list_init (&swap_free_list);
+}
+
+
+
+/* Store the data from the given kernel page into the swap space.
+   If successful, return the stored swap index; otherwise, return -1.  */
+index_t 
+swap_store (void *kpage)
+{
+  /* Ensure that the given kernel page address is valid. */
+  ASSERT (is_kernel_vaddr(kpage));
+
+  index_t index = get_free_swap_slot();
+
+  /* If no free slot is available. */
+  if (index == (index_t)-1)
+  {
+    return index;
+  }
+
+  /* Write the data from the kernel page into the swap space. */
+  for (int i = 0; i < BLOCK_PER_PAGE; i++) 
+  {
+    block_write(swap_block, index + i, kpage + i * BLOCK_SECTOR_SIZE);
+  }
+
+  return index;
+}
+
+
+/* Load data from the swap space identified by the given 
+   index into the provided kernel page. */
+void 
+swap_load (index_t index, void *kpage)
+{
+  /* Ensure that the swap index is valid. */
+  ASSERT (index != (index_t)-1);
+
+  /* Ensure that the kernel page address is valid. */
+  ASSERT (is_kernel_vaddr (kpage));
+
+  /* Ensure that the swap index is a multiple of BLOCK_PER_PAGE. */
+  ASSERT (index % BLOCK_PER_PAGE == 0);
+
+  /* Read the data from the swap space into the kernel page. */
+  for (int i = 0; i < BLOCK_PER_PAGE; i++) 
+  {
+    block_read (swap_block, index + i, kpage + i * BLOCK_SECTOR_SIZE);
+  }
+
+  swap_free(index);
+}
+
+
+/* Free a swap slot identified by the given index. 
+   If the index corresponds to the last allocated block, update top_index.
+   Otherwise, add the index to the free list for future reuse. */
+void 
+swap_free(index_t index)
+{
+  /* Ensure that the index is a multiple of BLOCK_PER_PAGE. */
+  ASSERT(index % BLOCK_PER_PAGE == 0);
+
+  /* If the index is the last allocated block, update top_index. */
+  if (top_index == index + BLOCK_PER_PAGE) 
+  {
+    top_index = index;
+  } 
+  else 
+  {
+    /* Otherwise, add the index to the swap free list for future reuse. */
+    struct swap_item *t = malloc(sizeof(struct swap_item));
+    t->index = index;
+    list_push_back(&swap_free_list, &t->list_elem);
+  }
+}
+
+/* Get a free swap slot.
+   If available in the swap free list, return it.
+   Otherwise, allocate a new slot from the swap block. */
+index_t 
+get_free_swap_slot ()
+{
+  /* Initialize to an invalid index. */
+  index_t res = (index_t) -1;
+
+  /* If the swap free list is empty, allocate a new slot from the swap block. */
+  if (list_empty (&swap_free_list)) 
+  {
+    /* Check if there are enough blocks available in the swap block. */
+    if (top_index + BLOCK_PER_PAGE < block_size(swap_block)) 
+    {
+      res = top_index;
+      top_index += BLOCK_PER_PAGE;  /* Increment top_index to reserve blocks. */
+    }
+  } 
+  else 
+  {
+    /* Otherwise, reuse a free slot from the swap free list. */
+    struct swap_item *t = list_entry(list_front(&swap_free_list), struct swap_item, list_elem);
+    list_remove(&t->list_elem);  /* Remove the item from the free list. */
+    res = t->index;
+    free (t);
+  }
+
+  return res;
+}
+
+
+/* ----- Newly Add for Proj03 VM ----- */
diff --git a/src/vm/swap.h b/src/vm/swap.h
new file mode 100644
index 0000000..af40a08
--- /dev/null
+++ b/src/vm/swap.h
@@ -0,0 +1,29 @@
+/* ----- Newly Add for Proj03 VM ----- */
+
+#ifndef MYPINTOS_SWAP_H
+#define MYPINTOS_SWAP_H
+
+#include "../devices/block.h"
+
+//identifier type of the swap slot
+typedef  uint32_t index_t;
+
+//initialize swap when kernel starts
+void swap_init();
+
+//store the content of a kpage(frame) to a swap slot(on the disk)
+//return an identifier of the swap slot
+index_t swap_store(void *kpage);
+
+//load a swap slot to the kpage(frame)
+//index must be got from swap_store()
+void swap_load(index_t index, void *kpage);
+
+//free a swap slot whose identifier is index
+//index must be got from swap_store()
+void swap_free(index_t index);
+
+
+#endif
+
+/* ----- Newly Add for Proj03 VM ----- */
diff --git a/src/vm/vm.tmpl b/src/vm/vm.tmpl
new file mode 100644
index 0000000..a5674b0
--- /dev/null
+++ b/src/vm/vm.tmpl
@@ -0,0 +1,155 @@
+       	       	    +---------------------------+
+		    |		CS 140		|
+		    | PROJECT 3: VIRTUAL MEMORY	|
+		    |	   DESIGN DOCUMENT	|
+		    +---------------------------+
+
+---- GROUP ----
+
+>> Fill in the names and email addresses of your group members.
+
+FirstName LastName <email@domain.example>
+FirstName LastName <email@domain.example>
+FirstName LastName <email@domain.example>
+
+---- PRELIMINARIES ----
+
+>> If you have any preliminary comments on your submission, notes for the
+>> TAs, or extra credit, please give them here.
+
+>> Please cite any offline or online sources you consulted while
+>> preparing your submission, other than the Pintos documentation, course
+>> text, lecture notes, and course staff.
+
+			PAGE TABLE MANAGEMENT
+			=====================
+
+---- DATA STRUCTURES ----
+
+>> A1: Copy here the declaration of each new or changed `struct' or
+>> `struct' member, global or static variable, `typedef', or
+>> enumeration.  Identify the purpose of each in 25 words or less.
+
+---- ALGORITHMS ----
+
+>> A2: In a few paragraphs, describe your code for locating the frame,
+>> if any, that contains the data of a given page.
+
+>> A3: How does your code coordinate accessed and dirty bits between
+>> kernel and user virtual addresses that alias a single frame, or
+>> alternatively how do you avoid the issue?
+
+---- SYNCHRONIZATION ----
+
+>> A4: When two user processes both need a new frame at the same time,
+>> how are races avoided?
+
+---- RATIONALE ----
+
+>> A5: Why did you choose the data structure(s) that you did for
+>> representing virtual-to-physical mappings?
+
+		       PAGING TO AND FROM DISK
+		       =======================
+
+---- DATA STRUCTURES ----
+
+>> B1: Copy here the declaration of each new or changed `struct' or
+>> `struct' member, global or static variable, `typedef', or
+>> enumeration.  Identify the purpose of each in 25 words or less.
+
+---- ALGORITHMS ----
+
+>> B2: When a frame is required but none is free, some frame must be
+>> evicted.  Describe your code for choosing a frame to evict.
+
+>> B3: When a process P obtains a frame that was previously used by a
+>> process Q, how do you adjust the page table (and any other data
+>> structures) to reflect the frame Q no longer has?
+
+>> B4: Explain your heuristic for deciding whether a page fault for an
+>> invalid virtual address should cause the stack to be extended into
+>> the page that faulted.
+
+---- SYNCHRONIZATION ----
+
+>> B5: Explain the basics of your VM synchronization design.  In
+>> particular, explain how it prevents deadlock.  (Refer to the
+>> textbook for an explanation of the necessary conditions for
+>> deadlock.)
+
+>> B6: A page fault in process P can cause another process Q's frame
+>> to be evicted.  How do you ensure that Q cannot access or modify
+>> the page during the eviction process?  How do you avoid a race
+>> between P evicting Q's frame and Q faulting the page back in?
+
+>> B7: Suppose a page fault in process P causes a page to be read from
+>> the file system or swap.  How do you ensure that a second process Q
+>> cannot interfere by e.g. attempting to evict the frame while it is
+>> still being read in?
+
+>> B8: Explain how you handle access to paged-out pages that occur
+>> during system calls.  Do you use page faults to bring in pages (as
+>> in user programs), or do you have a mechanism for "locking" frames
+>> into physical memory, or do you use some other design?  How do you
+>> gracefully handle attempted accesses to invalid virtual addresses?
+
+---- RATIONALE ----
+
+>> B9: A single lock for the whole VM system would make
+>> synchronization easy, but limit parallelism.  On the other hand,
+>> using many locks complicates synchronization and raises the
+>> possibility for deadlock but allows for high parallelism.  Explain
+>> where your design falls along this continuum and why you chose to
+>> design it this way.
+
+			 MEMORY MAPPED FILES
+			 ===================
+
+---- DATA STRUCTURES ----
+
+>> C1: Copy here the declaration of each new or changed `struct' or
+>> `struct' member, global or static variable, `typedef', or
+>> enumeration.  Identify the purpose of each in 25 words or less.
+
+---- ALGORITHMS ----
+
+>> C2: Describe how memory mapped files integrate into your virtual
+>> memory subsystem.  Explain how the page fault and eviction
+>> processes differ between swap pages and other pages.
+
+>> C3: Explain how you determine whether a new file mapping overlaps
+>> any existing segment.
+
+---- RATIONALE ----
+
+>> C4: Mappings created with "mmap" have similar semantics to those of
+>> data demand-paged from executables, except that "mmap" mappings are
+>> written back to their original files, not to swap.  This implies
+>> that much of their implementation can be shared.  Explain why your
+>> implementation either does or does not share much of the code for
+>> the two situations.
+
+			   SURVEY QUESTIONS
+			   ================
+
+Answering these questions is optional, but it will help us improve the
+course in future quarters.  Feel free to tell us anything you
+want--these questions are just to spur your thoughts.  You may also
+choose to respond anonymously in the course evaluations at the end of
+the quarter.
+
+>> In your opinion, was this assignment, or any one of the three problems
+>> in it, too easy or too hard?  Did it take too long or too little time?
+
+>> Did you find that working on a particular part of the assignment gave
+>> you greater insight into some aspect of OS design?
+
+>> Is there some particular fact or hint we should give students in
+>> future quarters to help them solve the problems?  Conversely, did you
+>> find any of our guidance to be misleading?
+
+>> Do you have any suggestions for the TAs to more effectively assist
+>> students, either for future quarters or the remaining projects?
+
+>> Any other comments?
-- 
GitLab