+
+Welcome to the Operating System Development course at Vienna. In this
+course, you will be working with an adapted version of the Pintos
+operating system, which was written by Ben Pfaff (See section 1.4 Acknowledgements.)
+
+
+So ... Welcome to Pintos. Pintos is a simple operating system framework for
+the 80x86 architecture. It supports kernel threads, loading and
+running user programs, and a file system, but it implements all of
+these in a very simple way. In the Pintos projects, you and your
+project team will strengthen its support in some of these areas.
+
+
+
+Pintos could, theoretically, run on a regular IBM-compatible PC.
+For simplicity, we will run Pintos projects in a system simulator, that is,
+a program that simulates an 80x86 CPU and its peripheral devices accurately
+enough that unmodified operating systems and software can run under it.
+In class we will use the
+Bochs and
+QEMU simulators. Pintos has also been tested with
+VMware Player.
+
+
+
+The course at the University of Stanford, where pintos originated, has a
+reputation of taking a lot of time -- we suppose deservedly so.
+We will do what we can to reduce the workload, such as providing a lot
+of support material, but there is plenty of hard work that needs to be done.
+We welcome your feedback. If you have suggestions on how we can reduce the
+unnecessary overhead of assignments, cutting them down to the important
+underlying issues, please let us know.
+
+
+
+This chapter explains how to get started working with Pintos. You
+should read the entire chapter before you start work on any of the
+projects.
+
+
+
+
+
+
+
1.1 Getting Started
+
+
+
+To get started, you'll have to log into a machine that Pintos can be
+built on. There are two possibilities: Either you work on one of the
+machines in the TILAB (ssh.tilab.tuwien.ac.at), or you use
+a virtual machine such as KVM, VMWare Player or VirtualBox. You may
+also setup your own machine to build pintos, but in this case we
+cannot provide support for problems you might encounter.
+We will test your submission in the TILAB environment, and thus you should
+ensure that your code works there.
+
+
+
+Once you've logged into one of these machines, either locally or
+remotely, start out by extracting the pintos tarball, and adding
+our binaries directory to your PATH environment variable.
+
+
+
+Assuming that you extracted the pintos tarball to $HOME/pintos,
+you need to add pintos' utils directory to your PATH
+environment variable.
+
PATH=$HOME/pintos/src/utils:$PATH
+
It is a good idea to add this line to your $HOME/.bash_profile
+startup script (or an equivalent script, if you do not happen to use bash).
+Otherwise, you'll have to type it every time you log in.
+
+
+
+
+
+
1.1.1 Source Tree Overview
+
+
+
+Here's the directory structure that you should see in pintos/src:
+
+
+
+
+
+
+
intro/
+
This directory is used to build the system and run tests for project 0.
+It only contains a Makefile, which describes how to build the system
+and run tests.
+
+
+
+
threads/
+
Source code for the base kernel, which is the focus of project 1.
+
+
+
+
userprog/
+
Source code for the user programs. You will complete the user program
+loader in project 0, and rely on the code in this directory in
+project 2.
+
+
+
+
vm/
+
An almost empty directory. You will implement virtual memory here in
+project 2.
+
+
+
+
filesys/
+
Source code for a basic file system. You will use this file system,
+but do not need to modify it in this course.
+
+
+
+
devices/
+
Source code for I/O device interfacing: keyboard, timer, disk, etc.
+You will modify the timer implementation in project 0. Otherwise
+you should have no need to change this code.
+
+
+
+
lib/
+
An implementation of a subset of the standard C library. The code in
+this directory is compiled into both the Pintos kernel and user
+programs that run under it. In both kernel code
+and user programs, headers in this directory can be included using the
+#include <...> notation. You should have little need to
+modify this code.
+
+
+
+
lib/kernel/
+
Parts of the C library that are included only in the Pintos kernel.
+This also includes implementations of some data types that you are
+free to use in your kernel code: bitmaps, doubly linked lists, and
+hash tables. In the kernel, headers in this
+directory can be included using the #include <...>
+notation.
+
+
+
+
lib/user/
+
Parts of the C library that are included only in Pintos user programs.
+In user programs, headers in this directory can be included using the
+#include <...> notation.
+
+
+
+
tests/
+
Tests for each project. You can modify this code if it helps you test
+your submission, but we will replace it with the originals before we run
+the tests.
+
+
+
+
examples/
+
Example user programs for use in project 0, and also project 2.
+
+
+
+
misc/
+
utils/
+
These files may come in handy if you decide to try working with Pintos
+on your own machine. Otherwise, you can ignore them.
+
+
+
+
+
+
+
1.1.2 Building Pintos
+
+
+
+As the next step, build the source code supplied for
+the first project. First, cd into the intro
+directory. Then, issue the make command. This will create a
+build directory under intro, populate it with a
+Makefile and a few subdirectories, and then build the kernel
+inside. The entire build should take less than 30 seconds.
+
+
+
+Following the build, the following are the interesting files in the
+build directory:
+
+
+
+
+
+
Makefile
+
A copy of pintos/src/Makefile.build. It describes how to build
+the kernel. See Adding Source Files, for more information.
+
+
+
+
kernel.o
+
Object file for the entire kernel. This is the result of linking
+object files compiled from each individual kernel source file into a
+single object file. It contains debug information, so you can run
+GDB (see section D.5 GDB) or backtrace (see section D.4 Backtraces) on it.
+
+
+
+
kernel.bin
+
Memory image of the kernel, that is, the exact bytes loaded into
+memory to run the Pintos kernel. This is just kernel.o with
+debug information stripped out, which saves a lot of space, which in
+turn keeps the kernel from bumping up against a 512 kB size limit
+imposed by the kernel loader's design.
+
+
+
+
loader.bin
+
Memory image for the kernel loader, a small chunk of code written in
+assembly language that reads the kernel from disk into memory and
+starts it up. It is exactly 512 bytes long, a size fixed by the
+PC BIOS.
+
+
+
+Subdirectories of build contain object files (.o) and
+dependency files (.d), both produced by the compiler. The
+dependency files tell make which source files need to be
+recompiled when other source or header files are changed.
+
+
+
+
+
+
+
1.1.3 Running Pintos
+
+
+
+We've supplied a program for conveniently running Pintos in a simulator,
+called pintos. In the simplest case, you can invoke
+pintos as pintos argument.... Each
+argument is passed to the Pintos kernel for it to act on.
+
+
+
+Try it out. First cd into the newly created build
+directory. Then issue the command
+pintos -kernel-test run alarm-multiple,
+which passes the arguments run alarm-multiple to the Pintos
+kernel. In these arguments, run, together with the kernel
+option -kernel-test, instructs the kernel to run a test and
+alarm-multiple is the test to run.
+
+
+
+This command creates a bochsrc.txt file, which is needed for
+running Bochs, and then invoke Bochs. Bochs opens a new window that
+represents the simulated machine's display, and a BIOS message briefly
+flashes. Then Pintos boots and runs the alarm-multiple test
+program, which outputs a few screenfuls of text. When it's done, you
+can close Bochs by clicking on the "Power" button in the window's top
+right corner, or rerun the whole process by clicking on the "Reset"
+button just to its left. The other buttons are not very useful for our
+purposes.
+
+
+
+(If no window appeared at all, then you're probably logged in remotely and X
+forwarding is not set up correctly. In this case, you can fix your X
+setup, or you can use the -v option to disable X output:
+pintos -v -- -kernel-test run alarm-multiple.)
+
+
+
+The text printed by Pintos inside Bochs probably went by too quickly to
+read. However, you've probably noticed by now that the same text was
+displayed in the terminal you used to run pintos. This is
+because Pintos sends all output both to the VGA display and to the first
+serial port, and by default the serial port is connected to Bochs's
+stdin and stdout. You can log serial output to a file by
+redirecting at the
+command line, e.g. pintos run alarm-multiple > logfile.
+
+
+
+The pintos program offers several options for configuring the
+simulator or the virtual hardware. If you specify any options, they
+must precede the commands passed to the Pintos kernel and be separated
+from them by --, so that the whole command looks like
+pintos option... -- argument.... Invoke
+pintos without any arguments to see a list of available options.
+Options can select a simulator to use: the default is Bochs, but
+--qemu selects QEMU. You can run the simulator
+with a debugger (see section D.5 GDB). You can set the amount of memory to give
+the VM. Finally, you can select how you want VM output to be displayed:
+use -v to turn off the VGA display, -t to use your
+terminal window as the VGA display instead of opening a new window
+(Bochs only), or -s to suppress serial input from stdin
+and output to stdout.
+
+
+
+The Pintos kernel has commands and options other than run.
+These are not very interesting for now, but you can see a list of them
+using -h, e.g. pintos -h.
+
+
+
+
+
+
+
1.1.4 Debugging versus Testing
+
+
+
+When you're debugging code, it's useful to be able to run a
+program twice and have it do exactly the same thing. On second and
+later runs, you can make new observations without having to discard or
+verify your old observations. This property is called
+"reproducibility." One of the simulators that Pintos supports, Bochs,
+can be set up for
+reproducibility, and that's the way that pintos invokes it
+by default.
+
+
+
+Of course, a simulation can only be reproducible from one run to the
+next if its input is the same each time. For simulating an entire
+computer, as we do, this means that every part of the computer must be
+the same. For example, you must use the same command-line argument, the
+same disks, the same version
+of Bochs, and you must not hit any keys on the keyboard (because you
+could not be sure to hit them at exactly the same point each time)
+during the runs.
+
+
+
+While reproducibility is useful for debugging, it is a problem for
+testing thread synchronization, an important part of most of the projects. In
+particular, when Bochs is set up for reproducibility, timer interrupts
+will come at perfectly reproducible points, and therefore so will
+thread switches. That means that running the same test several times
+doesn't give you any greater confidence in your code's correctness
+than does running it only once.
+
+
+
+So, to make your code easier to test, we've added a feature, called
+"jitter," to Bochs, that makes timer interrupts come at random
+intervals, but in a perfectly predictable way. In particular, if you
+invoke pintos with the option -j seed, timer
+interrupts will come at irregularly spaced intervals. Within a single
+seed value, execution will still be reproducible, but timer
+behavior will change as seed is varied. Thus, for the highest
+degree of confidence you should test your code with many seed values.
+
+
+
+On the other hand, when Bochs runs in reproducible mode, timings are not
+realistic, meaning that a "one-second" delay may be much shorter or
+even much longer than one second. You can invoke pintos with
+a different option, -r, to set up Bochs for realistic
+timings, in which a one-second delay should take approximately one
+second of real time. Simulation in real-time mode is not reproducible,
+and options -j and -r are mutually exclusive.
+
+
+
+The QEMU simulator is available as an
+alternative to Bochs (use --qemu when invoking
+pintos). The QEMU simulator is much faster than Bochs, but it
+only supports real-time simulation and does not have a reproducible
+mode.
+
+
+
+
+
+
+
1.2 Grading
+
+
+
+We will grade your assignments based on test results and design quality,
+inspecting both your implementation and your design documents.
+
+
+
+
+
+
+
1.2.1 Testing
+
+
+
+Your test result grade will be based on our tests. Each project has
+several tests, each of which has a name beginning with tests.
+To completely test your submission, invoke make check from the
+project build directory. This will build and run each test and
+print a "pass" or "fail" message for each one. When a test fails,
+make check also prints some details of the reason for failure.
+After running all the tests, make check also prints a summary
+of the test results.
+
+
+
+For project 1, the tests will probably run faster in Bochs. For the
+other projects, they will run much faster in QEMU.
+make check will select the faster simulator by default, but
+you can override its choice by specifying SIMULATOR=--bochs or
+SIMULATOR=--qemu on the make command line.
+
+
+
+You can also run individual tests one at a time. A given test t
+writes its output to t.output, then a script scores the
+output as "pass" or "fail" and writes the verdict to
+t.result. To run and grade a single test, make
+the .result file explicitly from the build directory, e.g.
+make tests/threads/alarm-multiple.result. If make says
+that the test result is up-to-date, but you want to re-run it anyway,
+either run make clean or delete the .output file by hand.
+
+
+
+By default, each test provides feedback only at completion, not during
+its run. If you prefer, you can observe the progress of each test by
+specifying VERBOSE=1 on the make command line, as in
+make check VERBOSE=1. You can also provide arbitrary options to the
+pintos run by the tests with PINTOSOPTS='...',
+e.g. make check PINTOSOPTS='-j 1' to select a jitter value of 1
+(see section 1.1.4 Debugging versus Testing).
+
+
+
+All of the tests and related files are in pintos/src/tests.
+Before we test your submission, we will replace the contents of that
+directory by a pristine, unmodified copy, to ensure that the correct
+tests are used. Thus, you can modify some of the tests if that helps in
+debugging, but we will run the originals.
+
+
+
+All software has bugs, so some of our tests may be flawed. If you think
+a test failure is a bug in the test, not a bug in your code,
+please point it out. We will look at it and fix it if necessary.
+
+
+
+Please don't try to take advantage of our generosity in giving out our
+test suite. Your code has to work properly in the general case, not
+just for the test cases we supply. For example, it would be unacceptable
+to explicitly base the kernel's behavior on the name of the running
+test case. Such attempts to side-step the test cases will receive no
+credit. If you think your solution may be in a gray area here, please
+ask us about it.
+
+
+
+
+
+
+
1.2.2 Design
+
+
+
+We will judge your design based on the design document and the source
+code that you submit. We will read your entire design document and much
+of your source code.
+
+
+
+Don't forget that design quality, including the design document, is 30%
+of your project grade. It
+is better to spend one or two hours writing a good design document than
+it is to spend that time getting the last 5% of the points for tests and
+then trying to rush through writing the design document in the last 15
+minutes.
+
+
+
+
+
+
+
1.2.2.1 Design Document
+
+
+
+We provide a design document template for each project. For each
+significant part of a project, the template asks questions in four
+areas:
+
+
+
+
+
+
Data Structures
+
+
+The instructions for this section are always the same:
+
+
+
+
+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.
+
+
+
+The first part is mechanical. Just copy new or modified declarations
+into the design document, to highlight for us the actual changes to data
+structures. Each declaration should include the comment that should
+accompany it in the source code (see below).
+
+
+
+We also ask for a very brief description of the purpose of each new or
+changed data structure. The limit of 25 words or less is a guideline
+intended to save your time and avoid duplication with later areas.
+
+
+
+
+
Algorithms
+
+
+This is where you tell us how your code works, through questions that
+probe your understanding of your code. We might not be able to easily
+figure it out from the code, because many creative solutions exist for
+most OS problems. Help us out a little.
+
+
+
+Your answers should be at a level below the high level description of
+requirements given in the assignment. We have read the assignment too,
+so it is unnecessary to repeat or rephrase what is stated there. On the
+other hand, your answers should be at a level above the low level of the
+code itself. Don't give a line-by-line run-down of what your code does.
+Instead, use your answers to explain how your code works to implement
+the requirements.
+
+
+
+
+
Synchronization
+
+
+An operating system kernel is a complex, multithreaded program, in which
+synchronizing multiple threads can be difficult. This section asks
+about how you chose to synchronize this particular type of activity.
+
+
+
+
+
Rationale
+
+
+Whereas the other sections primarily ask "what" and "how," the
+rationale section concentrates on "why." This is where we ask you to
+justify some design decisions, by explaining why the choices you made
+are better than alternatives. You may be able to state these in terms
+of time and space complexity, which can be made as rough or informal
+arguments (formal language or proofs are unnecessary).
+
+
+
+An incomplete, evasive, or non-responsive design document or one that
+strays from the template without good reason may be penalized.
+Incorrect capitalization, punctuation, spelling, or grammar can also
+cost points. See section C. Project Documentation, for a sample design document
+for a fictitious project.
+
+
+
+
+
+
+
1.2.2.2 Source Code
+
+
+
+Your design will also be judged by looking at your source code. We will
+typically look at the differences between the original Pintos source
+tree and your submission, based on the output of a command like
+diff -urpb pintos.orig pintos.submitted. We will try to match up your
+description of the design with the code submitted. Important
+discrepancies between the description and the actual code will be
+penalized, as will be any bugs we find by spot checks.
+
+
+
+The most important aspects of source code design are those that specifically
+relate to the operating system issues at stake in the project. For
+example, the organization of the supplemental page table is an important
+part of virtual memory design, so in the virtual memory project a poorly designed
+pagetable would lose points. Other issues are much less important. For
+example, multiple Pintos design problems call for a "priority
+queue," that is, a dynamic collection from which the minimum (or
+maximum) item can quickly be extracted. Fast priority queues can be
+implemented many ways, but we do not expect you to build a fancy data
+structure even if it might improve performance. Instead, you are
+welcome to use a linked list (and Pintos even provides one with
+convenient functions for sorting and finding minimums and maximums).
+
+
+
+Pintos is written in a consistent style. Make your additions and
+modifications in existing Pintos source files blend in, not stick out.
+In new source files, adopt the existing Pintos style by preference, but
+make your code self-consistent at the very least. There should not be a
+patchwork of different styles that makes it obvious that three different
+people wrote the code. Use horizontal and vertical white space to make
+code readable. Add a brief comment on every structure, structure
+member, global or static variable, typedef, enumeration, and function
+definition. Update
+existing comments as you modify code. Don't comment out or use the
+preprocessor to ignore blocks of code (instead, remove it entirely).
+Use assertions to document key invariants. Decompose code into
+functions for clarity. Code that is difficult to understand because it
+violates these or other "common sense" software engineering practices
+will be penalized.
+
+
+
+In the end, remember your audience. Code is written primarily to be
+read by humans. It has to be acceptable to the compiler too, but the
+compiler doesn't care about how it looks or how well it is written.
+
+
+
+
+
+
+
1.3 Legal and Ethical Issues
+
+
+
+Pintos is distributed under a liberal license that allows free use,
+modification, and distribution. Students and others who work on Pintos
+own the code that they write and may use it for any purpose.
+Pintos comes with NO WARRANTY, not even for MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE.
+See section License, for details of the license and lack of warranty.
+
+
+
+In the context of the Operating System Development at Vienna University
+of Technology, please respect the spirit and the letter of the honor code
+by refraining from reading any homework solutions available online or
+elsewhere. Reading the source code for other operating system kernels,
+such as Linux or FreeBSD, is allowed, but do not copy code from them literally.
+Please cite the code that inspired your own in your design documentation.
+Additionally, please do not redistribute the modified Pintos environment
+used in this course. It contains partial solutions which might spoil the
+fun for people at other universities.
+
+
+
+
+
+
+
1.4 Acknowledgements
+
+The Pintos core and this documentation were originally written by Ben
+Pfaff blp@cs.stanford.edu.
+
+
+Additional features were contributed by Anthony Romano
+chz@vt.edu.
+
+
+
+The GDB macros supplied with Pintos were written by Godmar Back
+gback@cs.vt.edu, and their documentation is adapted from his
+work.
+
+
+
+The original structure and form of Pintos was inspired by the Nachos
+instructional operating system from the University of California,
+Berkeley ([ Christopher]).
+
+
+
+The Pintos projects and documentation originated with those designed for
+Nachos by current and former CS 140 teaching assistants at Stanford
+University, including at least Yu Ping, Greg Hutchins, Kelly Shaw, Paul
+Twohey, Sameer Qureshi, and John Rector.
+
+
+
+Example code for monitors (see section A.3.4 Monitors) is
+from classroom slides originally by Dawson Engler and updated by Mendel
+Rosenblum.
+
+
+
+For the undergraduate OS Development course at Vienna UT, Rene Freingruber
+renefreing@yahoo.de evaluated Pintos, and provided information on
+expected work hours and typical pitfalls. Benedikt Huber
+benedikt@vmars.tuwien.ac.at adapted Pintos and its documentation to
+meet the requirements of the course; Roland Kammerer
+kammerer@vmars.tuwien.ac.at created the virtual machine environments
+to simplify working outside the lab.
+
+
+
+
+
+
+
1.5 Trivia
+
+
+
+Pintos originated as a replacement for Nachos with a similar design.
+Since then Pintos has greatly diverged from the Nachos design. Pintos
+differs from Nachos in two important ways. First, Pintos runs on real
+or simulated 80x86 hardware, but Nachos runs as a process on a
+host operating system. Second, Pintos is written in C like most
+real-world operating systems, but Nachos is written in C++.
+
+
+
+Why the name "Pintos"? First, like nachos, pinto beans are a common
+Mexican food. Second, Pintos is small and a "pint" is a small amount.
+Third, like drivers of the eponymous car, students are likely to have
+trouble with blow-ups.
+
+
+
+
+[IA32-v1].
+IA-32 Intel Architecture Software Developer's Manual Volume 1: Basic
+Architecture. Basic 80x86 architecture and programming
+environment. Available via developer.intel.com. Section numbers
+in this document refer to revision 18.
+
+
+
+
+[IA32-v2a].
+IA-32 Intel Architecture Software Developer's Manual
+Volume 2A: Instruction Set Reference A-M. 80x86 instructions
+whose names begin with A through M. Available via
+developer.intel.com. Section numbers in this document refer to
+revision 18.
+
+
+
+
+[IA32-v2b].
+IA-32 Intel Architecture Software Developer's Manual Volume 2B:
+Instruction Set Reference N-Z. 80x86 instructions whose names
+begin with N through Z. Available via developer.intel.com.
+Section numbers in this document refer to revision 18.
+
+
+
+
+[IA32-v3a].
+IA-32 Intel Architecture Software Developer's Manual Volume 3A: System
+Programming Guide. Operating system support, including segmentation,
+paging, tasks, interrupt and exception handling. Available via
+developer.intel.com. Section numbers in this document refer to
+revision 18.
+
+
+
+
+[FreeVGA].
+FreeVGA Project. Documents the VGA video
+hardware used in PCs.
+
+
+
+[Christopher].
+W. A. Christopher, S. J. Procter, T. E. Anderson,
+The Nachos instructional operating system.
+Proceedings of the USENIX Winter 1993 Conference.
+http://portal.acm.org/citation.cfm?id=1267307.
+
+
+
+
+[Dijkstra].
+E. W. Dijkstra, The structure of the "THE"
+multiprogramming system. Communications of the ACM 11(5):341--346,
+1968. http://doi.acm.org/10.1145/363095.363143.
+
+
+
+
+[Hoare].
+C. A. R. Hoare, Monitors: An Operating System
+Structuring Concept. Communications of the ACM, 17(10):549--557,
+1974. http://www.acm.org/classics/feb96/.
+
+
+
+
+[Lampson].
+B. W. Lampson, D. D. Redell, Experience with processes and
+monitors in Mesa. Communications of the ACM, 23(2):105--117, 1980.
+http://doi.acm.org/10.1145/358818.358824.
+
+
+
+
+[McKusick].
+M. K. McKusick, K. Bostic, M. J. Karels, J. S. Quarterman,
+The Design and Implementation of the 4.4BSD Operating
+System. Addison-Wesley, 1996.
+
+
+
+
+[Wilson].
+P. R. Wilson, M. S. Johnstone, M. Neely, D. Boles,
+Dynamic Storage Allocation: A Survey and Critical Review.
+International Workshop on Memory Management, 1995.
+http://www.cs.utexas.edu/users/oops/papers.html#allocsrv.
+
+
+
+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:
+
+
+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.
+
+
+In this assignment, you will learn about the existing functionality
+in Pintos, and add two small features to the system: a more efficient
+implementation of sleep, and the ability to pass command line
+arguments to user programs.
+
+
+
+You will be working in the threads directory for the first part
+of this assignment (with some work in the devices directory on
+the side), and modify the file userprog/process.c in the second part.
+
+
+
+The tests for Project 0 are executed by changing the working directory
+to intro and running make followed by make check.
+
+
+The first step is to read and understand the code for the initial thread
+system.
+Pintos already implements thread creation and thread completion,
+a simple scheduler to switch between threads, and synchronization
+primitives (semaphores, locks, condition variables, and optimization
+barriers).
+
+
+
+Some of this code might seem slightly mysterious. If
+you haven't already compiled and run the base system, as described in
+the introduction (see section 1. Introduction), you should do so now. You
+can read through parts of the source code to see what's going
+on. If you like, you can add calls to printf() almost
+anywhere, then recompile and run to see what happens and in what
+order. You can also run the kernel in a debugger and set breakpoints
+at interesting spots, single-step through code and examine data, and
+so on.
+
+
+
+When a thread is created, you are creating a new context to be
+scheduled. You provide a function to be run in this context as an
+argument to thread_create(). The first time the thread is
+scheduled and runs, it starts from the beginning of that function
+and executes in that context. When the function returns, the thread
+terminates. Each thread, therefore, acts like a mini-program running
+inside Pintos, with the function passed to thread_create()
+acting like main().
+
+
+
+At any given time, exactly one thread runs and the rest, if any,
+become inactive. The scheduler decides which thread to
+run next. (If no thread is ready to run
+at any given time, then the special "idle" thread, implemented in
+idle(), runs.)
+Synchronization primitives can force context switches when one
+thread needs to wait for another thread to do something.
+
+
+
+The mechanics of a context switch are
+in threads/switch.S, which is 80x86
+assembly code. (You don't have to understand it.) It saves the
+state of the currently running thread and restores the state of the
+thread we're switching to.
+
+
+
+Using the GDB debugger, slowly trace through a context
+switch to see what happens (see section D.5 GDB). You can set a
+breakpoint on schedule() to start out, and then
+single-step from there.(1) Be sure
+to keep track of each thread's address
+and state, and what procedures are on the call stack for each thread.
+You will notice that when one thread calls switch_threads(),
+another thread starts running, and the first thing the new thread does
+is to return from switch_threads(). You will understand the thread
+system once you understand why and how the switch_threads() that
+gets called is different from the switch_threads() that returns.
+See section A.2.3 Thread Switching, for more information.
+
+
+
+Warning: In Pintos, each thread is assigned a small,
+fixed-size execution stack just under 4 kB in size. The kernel
+tries to detect stack overflow, but it cannot do so perfectly. You
+may cause bizarre problems, such as mysterious kernel panics, if you
+declare large data structures as non-static local variables,
+e.g. int buf[1000];. Alternatives to stack allocation include
+the page allocator and the block allocator (see section A.5 Memory Allocation).
+
+
+
+
+
+
+
2.1.1 Source Files
+
+
+
+Here is a brief overview of the files in the threads
+directory. You will not need to modify most of this code, but the
+hope is that presenting this overview will give you a start on what
+code to look at.
+
+
+
+
+
+
loader.S
+
loader.h
+
The kernel loader. Assembles to 512 bytes of code and data that the
+PC BIOS loads into memory and which in turn finds the kernel on disk,
+loads it into memory, and jumps to start() in start.S.
+See section A.1.1 The Loader, for details. You should not need to look at
+this code or modify it.
+
+
+
+
start.S
+
Does basic setup needed for memory protection and 32-bit
+operation on 80x86 CPUs. Unlike the loader, this code is
+actually part of the kernel. See section A.1.2 Low-Level Kernel Initialization,
+for details.
+
+
+
+
kernel.lds.S
+
The linker script used to link the kernel. Sets the load address of
+the kernel and arranges for start.S to be near the beginning
+of the kernel image. See section A.1.1 The Loader, for details. Again, you
+should not need to look at this code
+or modify it, but it's here in case you're curious.
+
+
+
+
init.c
+
init.h
+
Kernel initialization, including main(), the kernel's "main
+program." You should look over main() at least to see what
+gets initialized. You might want to add your own initialization code
+here. See section A.1.3 High-Level Kernel Initialization, for details.
+
+
+
+
thread.c
+
thread.h
+
Basic thread support. Much of your work will take place in these files.
+thread.h defines struct thread, which you are likely to modify
+in all three projects. See A.2.1 struct thread and A.2 Threads for
+more information.
+
+
+
+
switch.S
+
switch.h
+
Assembly language routine for switching threads. Already discussed
+above. See section A.2.2 Thread Functions, for more information.
+
+
+
+
palloc.c
+
palloc.h
+
Page allocator, which hands out system memory in multiples of 4 kB
+pages. See section A.5.1 Page Allocator, for more information.
+
+
+
+
malloc.c
+
malloc.h
+
A simple implementation of malloc() and free() for
+the kernel. See section A.5.2 Block Allocator, for more information.
+
+
+
+
interrupt.c
+
interrupt.h
+
Basic interrupt handling and functions for turning interrupts on and
+off. See section A.4 Interrupt Handling, for more information.
+
Basic synchronization primitives: semaphores, locks, condition
+variables, and optimization barriers. You will need to use these for
+synchronization in all
+four projects. See section A.3 Synchronization, for more information.
+
+
+
+
io.h
+
Functions for I/O port access. This is mostly used by source code in
+the devices directory that you won't have to touch.
+
+
+
+
vaddr.h
+
pte.h
+
Functions and macros for working with virtual addresses and page table
+entries. These will be more important to you in project 2. For now,
+you can ignore them.
+
+
+
+
flags.h
+
Macros that define a few bits in the 80x86 "flags" register.
+Probably of no interest. See [ IA32-v1], section 3.4.3, "EFLAGS
+Register," for more information.
+
+
+
+
+
+
+
2.1.1.1 devices code
+
+
+
+The basic threaded kernel also includes these files in the
+devices directory:
+
+
+
+
+
+
timer.c
+
timer.h
+
System timer that ticks, by default, 100 times per second. You will
+modify this code in this project.
+
+
+
+
vga.c
+
vga.h
+
VGA display driver. Responsible for writing text to the screen.
+You should have no need to look at this code. printf()
+calls into the VGA display driver for you, so there's little reason to
+call this code yourself.
+
+
+
+
serial.c
+
serial.h
+
Serial port driver. Again, printf() calls this code for you,
+so you don't need to do so yourself.
+It handles serial input by passing it to the input layer (see below).
+
+
+
+
block.c
+
block.h
+
An abstraction layer for block devices, that is, random-access,
+disk-like devices that are organized as arrays of fixed-size blocks.
+Out of the box, Pintos supports two types of block devices: IDE disks
+and partitions.
+
+
+
+
ide.c
+
ide.h
+
Supports reading and writing sectors on up to 4 IDE disks.
+
+
+
+
partition.c
+
partition.h
+
Understands the structure of partitions on disks, allowing a single
+disk to be carved up into multiple regions (partitions) for
+independent use.
+
+
+
+
kbd.c
+
kbd.h
+
Keyboard driver. Handles keystrokes passing them to the input layer
+(see below).
+
+
+
+
input.c
+
input.h
+
Input layer. Queues input characters passed along by the keyboard or
+serial drivers.
+
+
+
+
intq.c
+
intq.h
+
Interrupt queue, for managing a circular queue that both kernel
+threads and interrupt handlers want to access. Used by the keyboard
+and serial drivers.
+
+
+
+
rtc.c
+
rtc.h
+
Real-time clock driver, to enable the kernel to determine the current
+date and time. By default, this is only used by thread/init.c
+to choose an initial seed for the random number generator.
+
+
+
+
speaker.c
+
speaker.h
+
Driver that can produce tones on the PC speaker.
+
+
+
+
pit.c
+
pit.h
+
Code to configure the 8254 Programmable Interrupt Timer. This code is
+used by both devices/timer.c and devices/speaker.c
+because each device uses one of the PIT's output channel.
+
+
+
+
+
+
+
2.1.1.2 lib files
+
+
+
+Finally, lib and lib/kernel contain useful library
+routines. (lib/user will be used by user programs, starting in
+project 2, but it is not part of the kernel.) Here's a few more
+details:
+
+
+
+
+
+
ctype.h
+
inttypes.h
+
limits.h
+
stdarg.h
+
stdbool.h
+
stddef.h
+
stdint.h
+
stdio.c
+
stdio.h
+
stdlib.c
+
stdlib.h
+
string.c
+
string.h
+
A subset of the standard C library. See section B.2 C99, for
+information
+on a few recently introduced pieces of the C library that you might
+not have encountered before. See section B.3 Unsafe String Functions, for
+information on what's been intentionally left out for safety.
+
+
+
+
debug.c
+
debug.h
+
Functions and macros to aid debugging. See section D. Debugging Tools, for
+more information.
+
+
+
+
random.c
+
random.h
+
Pseudo-random number generator. The actual sequence of random values
+will not vary from one Pintos run to another, unless you do one of
+three things: specify a new random seed value on the -rs
+kernel command-line option on each run, or use a simulator other than
+Bochs, or specify the -r option to pintos.
+
+
+
+
round.h
+
Macros for rounding.
+
+
+
+
syscall-nr.h
+
System call numbers. Not used until project 2.
+
+
+
+
kernel/list.c
+
kernel/list.h
+
Doubly linked list implementation. Used all over the Pintos code, and
+you'll probably want to use it a few places yourself.
+
+
+
+
kernel/bitmap.c
+
kernel/bitmap.h
+
Bitmap implementation. You can use this in your code if you like, but
+you probably won't have any need for it in project 0 and project 1.
+
+
+
+
kernel/hash.c
+
kernel/hash.h
+
Hash table implementation.
+
+
+
+
kernel/console.c
+
kernel/console.h
+
kernel/stdio.h
+
Implements printf() and a few other functions.
+
+
+
+
+
+
+
2.1.2 Synchronization
+
+
+
+Proper synchronization is an important part of the solutions to these
+problems. Any synchronization problem can be easily solved by turning
+interrupts off: while interrupts are off, there is no concurrency, so
+there's no possibility for race conditions. Therefore, it's tempting to
+solve all synchronization problems this way, but don't.
+Instead, use semaphores, locks, and condition variables to solve the
+bulk of your synchronization problems. Read the tour section on
+synchronization (see section A.3 Synchronization) or the comments in
+threads/synch.c if you're unsure what synchronization primitives
+may be used in what situations.
+
+
+
+In the Pintos projects, the only class of problem best solved by
+disabling interrupts is coordinating data shared between a kernel thread
+and an interrupt handler. Because interrupt handlers can't sleep, they
+can't acquire locks. This means that data shared between kernel threads
+and an interrupt handler must be protected within a kernel thread by
+turning off interrupts.
+
+
+
+This project only requires accessing a little bit of thread state from
+interrupt handlers. For the alarm clock, the timer interrupt needs to
+wake up sleeping threads. In the advanced scheduler, the timer
+interrupt needs to access a few global and per-thread variables. When
+you access these variables from kernel threads, you will need to disable
+interrupts to prevent the timer interrupt from interfering.
+
+
+
+When you do turn off interrupts, take care to do so for the least amount
+of code possible, or you can end up losing important things such as
+timer ticks or input events. Turning off interrupts also increases the
+interrupt handling latency, which can make a machine feel sluggish if
+taken too far.
+
+
+
+The synchronization primitives themselves in synch.c are
+implemented by disabling interrupts. You may need to increase the
+amount of code that runs with interrupts disabled here, but you should
+still try to keep it to a minimum.
+
+
+
+Disabling interrupts can be useful for debugging, if you want to make
+sure that a section of code is not interrupted. You should remove
+debugging code before turning in your project. (Don't just comment it
+out, because that can make the code difficult to read.)
+
+
+
+There should be no busy waiting in your submission. A tight loop that
+calls thread_yield() is one form of busy waiting.
+
+
+
+
+
+
+
2.1.3 Development Suggestions
+
+
+
+In the past, many groups divided the assignment into pieces, then each
+group member worked on his or her piece until just before the
+deadline, at which time the group reconvened to combine their code and
+submit. This is a bad idea. We do not recommend this
+approach. Groups that do this often find that two changes conflict
+with each other, requiring lots of last-minute debugging. Some groups
+who have done this have turned in code that did not even compile or
+boot, much less pass any tests.
+
+
+
+Instead, we recommend integrating your team's changes early and often,
+using the source code control system git.
+This is less likely to produce surprises, because everyone can see
+everyone else's code as it is written, instead of just when it is
+finished. These systems also make it possible to review changes and,
+when a change introduces a bug, drop back to working versions of code.
+
+
+
+You should expect to run into bugs that you simply don't understand
+while working on this and subsequent projects. When you do,
+reread the appendix on debugging tools, which is filled with
+useful debugging tips that should help you to get back up to speed
+(see section D. Debugging Tools). Be sure to read the section on backtraces
+(see section D.4 Backtraces), which will help you to get the most out of every
+kernel panic or assertion failure.
+
+
+
+
+
+
+
2.2 Understanding User Programs
+
+
+
+The tests for both the alarm clock assignment in Project 0, and the
+priority scheduler in Project 1, run as part of the operating system
+kernel, with full access to privileged parts of the system.
+Once we start running user programs on top of the operating system, this
+is no longer true.
+
+
+
+We allow more than one process to run at a time. Each process has one
+thread (multithreaded processes are not supported). User programs are
+written under the illusion that they have the entire machine. This
+means that when you load and run multiple processes at a time, you must
+manage memory, scheduling, and other state correctly to maintain this
+illusion.
+
+
+
+In Project 2, we will test your operating system by running
+user programs. This gives you much greater freedom. You must make sure
+that the user program interface meets the specifications described here,
+but given that constraint you are free to restructure or rewrite kernel
+code however you wish.
+
+
+
+
+
+
+
2.2.1 Source Files
+
+
+
+
+
+
process.c
+
process.h
+
Loads ELF binaries and starts processes.
+
+
+
+
pagedir.c
+
pagedir.h
+
A simple manager for 80x86 hardware page tables.
+Although you probably won't want to modify this code for this project,
+you may want to call some of its functions.
+See Page Tables, for more information.
+
+
+
+
syscall.c
+
syscall.h
+
Whenever a user process wants to access some kernel functionality, it
+invokes a system call.
+
+
+
+
exception.c
+
exception.h
+
When a user process performs a privileged or prohibited operation, it
+traps into the kernel as an "exception" or "fault."(2) These files handle
+exceptions. In project 2, you will need to modify the page fault
+handler to support lazy page loading and stack growth.
+
+
+
+
gdt.c
+
gdt.h
+
The 80x86 is a segmented architecture. The Global Descriptor
+Table (GDT) is a table that describes the segments in use. These
+files set up the GDT. You should not need to modify these
+files for any of the projects. You can read the code if
+you're interested in how the GDT works.
+
+
+
+
tss.c
+
tss.h
+
The Task-State Segment (TSS) is used for 80x86 architectural
+task switching. Pintos uses the TSS only for switching stacks when a
+user process enters an interrupt handler, as does Linux. You
+should not need to modify these files for any of the projects.
+You can read the code if you're interested in how the TSS
+works.
+
+
+
+
+
+
+
2.2.2 Using the File System
+
+
+
+You will need to interface to the file system code, because
+user programs are loaded from the file system and most of the
+system calls you must implement deal with the file system.
+You will want to look over the filesys.h and file.h
+interfaces to understand how to use the file system, and especially
+its many limitations.
+
+
+
+There is no need to modify the file system code in this course, and so
+we recommend that you do not. Working on the file system is likely to
+distract you from the project's foci.
+
+
+
+You will have to tolerate the following limitations, however:
+
+
+
+
+
+No internal synchronization. Concurrent accesses will interfere with one
+another. You should use synchronization to ensure that only one process at a
+time is executing file system code.
+
+
+
+
+File size is fixed at creation time. The root directory is
+represented as a file, so the number of files that may be created is also
+limited.
+
+
+
+
+File data is allocated as a single extent, that is, data in a single
+file must occupy a contiguous range of sectors on disk. External
+fragmentation can therefore become a serious problem as a file system is
+used over time.
+
+
+
+
+No subdirectories.
+
+
+
+
+File names are limited to 14 characters.
+
+
+
+
+A system crash mid-operation may corrupt the disk in a way
+that cannot be repaired automatically. There is no file system repair
+tool anyway.
+
+
+
+One important feature is included:
+
+
+
+
+
+Unix-like semantics for filesys_remove() are implemented.
+That is, if a file is open when it is removed, its blocks
+are not deallocated and it may still be accessed by any
+threads that have it open, until the last one closes it. See Removing an Open File, for more information.
+
+
+
+You need to be able to create a simulated disk with a file system
+partition. The pintos-mkdisk program provides this
+functionality. From the userprog/build directory, execute
+pintos-mkdisk filesys.dsk --filesys-size=2. This command
+creates a simulated disk named filesys.dsk that contains a 2
+MB Pintos file system partition. Then format the file system
+partition by passing -f -q on the kernel's command line:
+pintos -f -q. The -f option causes the file system to
+be formatted, and -q causes Pintos to exit as soon as the
+format is done.
+
+
+
+You'll need a way to copy files in and out of the simulated file system.
+The pintos-p ("put") and -g ("get")
+options do this. To copy file into the
+Pintos file system, use the command pintos -p file -- -q.
+(The -- is needed because -p is for the pintos
+script, not for the simulated kernel.) To copy it to the Pintos file
+system under the name newname, add -a
+newname: pintos -p file -a newname -- -q. The
+commands for copying files out of a VM are similar, but substitute
+-g for -p.
+
+
+
+Incidentally, these commands work by passing special commands
+extract and append on the kernel's command line and copying
+to and from a special simulated "scratch" partition. If you're very
+curious, you can look at the pintos script as well as
+filesys/fsutil.c to learn the implementation details.
+
+
+
+Here's a summary of how to create a disk with a file system partition,
+format the file system, copy the echo program into the new
+disk, and then run echo, passing argument x.
+(Argument passing won't work until you implemented it.) It assumes
+that you've already built the examples in examples and that the
+current directory is userprog/build:
+
+
+
+
pintos-mkdisk filesys.dsk --filesys-size=2
+pintos -f -q
+pintos -p ../../examples/echo -a echo -- -q
+pintos -q run 'echo x'
+
+
+The three final steps can actually be combined into a single command:
+
+
+
+
pintos-mkdisk filesys.dsk --filesys-size=2
+pintos -p ../../examples/echo -a echo -- -f -q run 'echo x'
+
+
+If you don't want to keep the file system disk around for later use or
+inspection, you can even combine all four steps into a single command.
+The --filesys-size=n option creates a temporary file
+system partition
+approximately n megabytes in size just for the duration of the
+pintos run. The Pintos automatic test suite makes extensive
+use of this syntax:
+
+
+
+
pintos --filesys-size=2 -p ../../examples/echo -a echo -- -f -q run 'echo x'
+
+
+You can delete a file from the Pintos file system using the rm
+file kernel action, e.g. pintos -q rm file. Also,
+ls lists the files in the file system and cat
+file prints a file's contents to the display.
+
+
+
+
+
+
+
2.2.3 How User Programs Work
+
+
+
+Pintos can run normal C programs, as long as they fit into memory and use
+only the system calls you implement. Notably, malloc() cannot be
+implemented because none of the system calls required for this project
+allow for memory allocation. Pintos also can't run programs that use
+floating point operations, since the kernel doesn't save and restore the
+processor's floating-point unit when switching threads.
+
+
+
+The src/examples directory contains a few sample user
+programs. The Makefile in this directory
+compiles the provided examples, and you can edit it
+compile your own programs as well. Some of the example programs will
+not work with the current implementation of Pintos.
+
+
+
+Pintos can load ELF executables with the loader provided for you
+in userprog/process.c. ELF is a file format used by Linux,
+Solaris, and many other operating systems for object files,
+shared libraries, and executables. You can actually use any compiler
+and linker that output 80x86 ELF executables to produce programs
+for Pintos. (We've provided compilers and linkers that should do just
+fine.)
+
+
+
+You should realize immediately that, until you copy a
+test program to the simulated file system, Pintos will be unable to do
+useful work. You won't be able to do
+interesting things until you copy a variety of programs to the file system.
+You might want to create a clean reference file system disk and copy that
+over whenever you trash your filesys.dsk beyond a useful state,
+which may happen occasionally while debugging.
+
+
+
+
+
+
+
2.2.4 Virtual Memory Layout
+
+
+
+Virtual memory in Pintos is divided into two regions: user virtual
+memory and kernel virtual memory. User virtual memory ranges from
+virtual address 0 up to PHYS_BASE, which is defined in
+threads/vaddr.h and defaults to 0xc0000000 (3 GB). Kernel
+virtual memory occupies the rest of the virtual address space, from
+PHYS_BASE up to 4 GB.
+
+
+
+User virtual memory is per-process.
+When the kernel switches from one process to another, it
+also switches user virtual address spaces by changing the processor's
+page directory base register (see pagedir_activate() in
+userprog/pagedir.c). struct thread contains a pointer to a
+process's page table.
+
+
+
+Kernel virtual memory is global. It is always mapped the same way,
+regardless of what user process or kernel thread is running. In
+Pintos, kernel virtual memory is mapped one-to-one to physical
+memory, starting at PHYS_BASE. That is, virtual address
+PHYS_BASE accesses physical
+address 0, virtual address PHYS_BASE + 0x1234 accesses
+physical address 0x1234, and so on up to the size of the machine's
+physical memory.
+
+
+
+A user program can only access its own user virtual memory. An attempt to
+access kernel virtual memory causes a page fault, handled by
+page_fault() in userprog/exception.c, and the process
+will be terminated. Kernel threads can access both kernel virtual
+memory and, if a user process is running, the user virtual memory of
+the running process. However, even in the kernel, an attempt to
+access memory at an unmapped user virtual address
+will cause a page fault.
+
+
+
+
+
+
+
2.2.4.1 Typical Memory Layout
+
+
+
+Conceptually, each process is
+free to lay out its own user virtual memory however it
+chooses. In practice, user virtual memory is laid out like this:
+
+
+In this project, the user stack is fixed in size, but in project 2 it
+will be allowed to grow. Traditionally, the size of the uninitialized
+data segment can be adjusted with a system call, but you will not have
+to implement this.
+
+
+
+The code segment in Pintos starts at user virtual address
+0x08084000, approximately 128 MB from the bottom of the address
+space. This value is specified in [ SysV-i386] and has no deep
+significance.
+
+
+
+The linker sets the layout of a user program in memory, as directed by a
+"linker script" that tells it the names and locations of the various
+program segments. You can learn more about linker scripts by reading
+the "Scripts" chapter in the linker manual, accessible via info
+ld.
+
+
+
+To view the layout of a particular executable, run objdump
+(80x86) or i386-elf-objdump (SPARC) with the -p
+option.
+
+
+
+
+
+
+
2.2.5 Accessing User Memory
+
+
+
+As part of a system
+call, the kernel must often access memory through pointers provided by a user
+program. The kernel must be very careful about doing so, because
+the user can pass a null pointer, a pointer to
+unmapped virtual memory, or a pointer to kernel virtual address space
+(above PHYS_BASE). All of these types of invalid pointers must
+be rejected without harm to the kernel or other running processes, by
+terminating the offending process and freeing its resources.
+
+
+
+There are at least two reasonable ways to do this correctly. The
+first method is to verify
+the validity of a user-provided pointer, then dereference it.
+The second method is to check only that a user
+pointer points below PHYS_BASE, then dereference it.
+An invalid user pointer will cause a "page fault" that you can
+handle by modifying the code for page_fault() in
+userprog/exception.c. This technique is normally faster
+because it takes advantage of the processor's MMU, so it tends to be
+used in real kernels (including Linux). It is also the way
+access to user pointers is implemented in the Pintos version provided.
+
+
+
+In either case, one needs to make sure not to "leak" resources. For
+example, suppose that your system call has acquired a lock or
+allocated memory with malloc(). If you encounter an invalid user pointer
+afterward, you must still be sure to release the lock or free the page
+of memory. If you choose to verify user pointers before dereferencing
+them, this should be straightforward. It's more difficult to handle
+if an invalid pointer causes a page fault,
+because there's no way to return an error code from a memory access.
+
+
+
+
+
+
+
2.3 Requirements
+
+
+
+
+
+
+
2.3.1 Design Document
+
+
+
+Before you turn in your project, you must copy the
+project 0 design document template into your source tree under the name
+pintos/src/intro/DESIGNDOC and fill it in. We recommend that
+you read the design document template before you start working on the
+project. See section C. Project Documentation, for a sample design document
+that goes along with a fictitious project.
+
+
+
+
+
+
+
2.3.2 Alarm Clock
+
+
+
+Reimplement timer_sleep(), defined in devices/timer.c.
+Although a working implementation is provided, it "busy waits," that
+is, it spins in a loop checking the current time and calling
+thread_yield() until enough time has gone by. Reimplement it to
+avoid busy waiting.
+
+
+
+
+
+
+
Function: void timer_sleep (int64_t ticks)
+
Suspends execution of the calling thread until time has advanced by at
+least x timer ticks. Unless the system is otherwise idle, the
+thread need not wake up after exactly x ticks. Just put it on
+the ready queue after they have waited for the right amount of time.
+
+
+timer_sleep() is useful for threads that operate in real-time,
+e.g. for blinking the cursor once per second.
+
+
+
+The argument to timer_sleep() is expressed in timer ticks, not in
+milliseconds or any another unit. There are TIMER_FREQ timer
+ticks per second, where TIMER_FREQ is a macro defined in
+devices/timer.h. The default value is 100. We don't recommend
+changing this value, because any change is likely to cause many of
+the tests to fail.
+
+
+
+
+Separate functions timer_msleep(), timer_usleep(), and
+timer_nsleep() do exist for sleeping a specific number of
+milliseconds, microseconds, or nanoseconds, respectively, but these will
+call timer_sleep() automatically when necessary. You do not need
+to modify them.
+
+
+
+If your delays seem too short or too long, reread the explanation of the
+-r option to pintos (see section 1.1.4 Debugging versus Testing).
+
+
+
+The tests for the 2.3.2 Alarm Clock assignment are executed by changing the
+working directory intro. The run make to build the
+Pintos kernel. Finally run make check to run the tests,
+followed by make grade to obtain your score.
+
+
+
+The alarm clock implementation is not needed for later projects.
+
+
+
+
+
+
+
2.3.3 Argument Passing
+
+
+
+Currently, process_execute() does not support passing arguments to
+new processes. Implement this functionality, by extending
+process_execute() so that instead of simply taking a program file
+name as its argument, it divides it into words at spaces. The first
+word is the program name, the second word is the first argument, and so
+on. That is, process_execute("grep foo bar") should run
+grep passing two arguments foo and bar.
+
+
+
+Within a command line, multiple spaces are equivalent to a single
+space, so that process_execute("grep foo bar")
+is equivalent to our original example. You can impose a reasonable
+limit on the length of the command line arguments. For example, you
+could limit the arguments to those that will fit in a single page (4
+kB). (There is an unrelated limit of 128 bytes on command-line
+arguments that the pintos utility can pass to the kernel.)
+
+
+
+You can parse argument strings any way you like. If you're lost,
+look at strtok_r(), prototyped in lib/string.h and
+implemented with thorough comments in lib/string.c. You can
+find more about it by looking at the man page (run man strtok_r
+at the prompt).
+
+
+Here's a summary of our reference solution, produced by the
+diffstat program. The final row gives total lines inserted
+and deleted; a changed line counts as both an insertion and a deletion.
+
+
+
+The reference solution represents just one possible solution. Many
+other solutions are also possible and many of those differ greatly from
+the reference solution. Some excellent solutions may not modify all the
+files modified by the reference solution, and some may modify files not
+modified by the reference solution.
+
How do I update the Makefiles when I add a new source file?
+
+
+
+To add a .c file, edit the top-level Makefile.build.
+Add the new file to variable dir_SRC, where
+dir is the directory where you added the file. For this
+project, that means you should add it to threads_SRC or
+devices_SRC. Then run make. If your new file
+doesn't get
+compiled, run make clean and then try again.
+
+
+
+When you modify the top-level Makefile.build and re-run
+make, the modified
+version should be automatically copied to
+threads/build/Makefile. The converse is
+not true, so any changes will be lost the next time you run make
+clean from the threads directory. Unless your changes are
+truly temporary, you should prefer to edit Makefile.build.
+
+
+
+A new .h file does not require editing the Makefiles.
+
+
+
+
+
What does warning: no previous prototype for `func' mean?
+
+
+It means that you defined a non-static function without
+preceding it by a prototype. Because non-static functions are
+intended for use by other .c files, for safety they should be
+prototyped in a header file included before their definition. To fix
+the problem, add a prototype in a header file that you include, or, if
+the function isn't actually used by other .c files, make it
+static.
+
+
+
+
+
What is the interval between timer interrupts?
+
+
+Timer interrupts occur TIMER_FREQ times per second. You can
+adjust this value by editing devices/timer.h. The default is
+100 Hz.
+
+
+
+We don't recommend changing this value, because any changes are likely
+to cause many of the tests to fail.
+
+
+
+
+
How long is a time slice?
+
+
+There are TIME_SLICE ticks per time slice. This macro is
+declared in threads/thread.c. The default is 4 ticks.
+
+
+
+We don't recommend changing this value, because any changes are likely
+to cause many of the tests to fail.
+
Do I need to account for timer values overflowing?
+
+
+Don't worry about the possibility of timer values overflowing. Timer
+values are expressed as signed 64-bit numbers, which at 100 ticks per
+second should be good for almost 2,924,712,087 years. By then, we
+expect Pintos to have been phased out of the curriculum.
+
+
+
+
+
+
+
2.4.3 Userprog FAQ
+
+
+
+
+
+
The kernel always panics when I run pintos -p file -- -q.
+
+
+Did you format the file system (with pintos -f)?
+
+
+
+Is your file name too long? The file system limits file names to 14
+characters. A command like pintos -p ../../examples/echo -- -q
+will exceed the limit. Use pintos -p ../../examples/echo -a echo
+-- -q to put the file under the name echo instead.
+
+
+
+Is the file system full?
+
+
+
+Does the file system already contain 16 files? The base Pintos file
+system has a 16-file limit.
+
+
+
+The file system may be so fragmented that there's not enough contiguous
+space for your file.
+
+
+
+
+
When I run pintos -p ../file --, file isn't copied.
+
+
+Files are written under the name you refer to them, by default, so in
+this case the file copied in would be named ../file. You
+probably want to run pintos -p ../file -a file -- instead.
+
+
+
+You can list the files in your file system with pintos -q ls.
+
+
+
+
+
All my user programs die with page faults.
+
+
+This will happen if you haven't implemented argument passing
+(or haven't done so correctly). The basic C library for user programs tries
+to read argc and argv off the stack. If the stack
+isn't properly set up, this causes a page fault.
+
+
+
+
+
How can I disassemble user programs?
+
+
+The objdump (80x86) or i386-elf-objdump
+(SPARC) utility can disassemble entire user
+programs or object files. Invoke it as objdump -d
+file. You can use GDB's
+disassemble command to disassemble individual functions
+(see section D.5 GDB).
+
+
+
+
+
Why do many C include files not work in Pintos programs?
+
Can I use libfoo in my Pintos programs?
+
+
+The C library we provide is very limited. It does not include many of
+the features that are expected of a real operating system's C library.
+The C library must be built specifically for the operating system (and
+architecture), since it must make system calls for I/O and memory
+allocation. (Not all functions do, of course, but usually the library
+is compiled as a unit.)
+
+
+
+The chances are good that the library you want uses parts of the C library
+that Pintos doesn't implement. It will probably take at least some
+porting effort to make it work under Pintos. Notably, the Pintos
+user program C library does not have a malloc() implementation.
+
+
+
+
+
How do I compile new user programs?
+
+
+Modify src/examples/Makefile, then run make.
+
+
+
+
+
Can I run user programs under a debugger?
+
+
+Yes, with some limitations. See section D.5 GDB.
+
+
+
+
+
How can I run user programs that need more than 4 kB stack space?
+
+
+You may modify the stack setup code to allocate more than one page of
+stack space for each process. In project 2, you will implement a better
+solution.
+
+
+
+
+
What happens when an open file is removed?
+
+
+
+You should implement the standard Unix semantics for files. That is, when
+a file is removed any process which has a file descriptor for that file
+may continue to use that descriptor. This means that
+they can read and write from the file. The file will not have a name,
+and no other processes will be able to open it, but it will continue
+to exist until all file descriptors referring to the file are closed
+or the machine shuts down.
+
+
+
+
+
+
+
+
+
+
2.4.4 Argument Passing FAQ
+
+
+
+
+
+
Isn't the top of stack in kernel virtual memory?
+
+
+The top of stack is at PHYS_BASE, typically 0xc0000000, which
+is also where kernel virtual memory starts.
+But before the processor pushes data on the stack, it decrements the stack
+pointer. Thus, the first (4-byte) value pushed on the stack
+will be at address 0xbffffffc.
+
+
+
+
+
Is PHYS_BASE fixed?
+
+
+No. You should be able to support PHYS_BASE values that are
+any multiple of 0x10000000 from 0x80000000 to 0xf0000000,
+simply via recompilation.
+
+
+
+
+
+
+
2.5 80x86 Calling Convention
+
+
+
+This section summarizes important points of the convention used for
+normal function calls on 32-bit 80x86 implementations of Unix.
+Some details are omitted for brevity. If you do want all the details,
+refer to [ SysV-i386].
+
+
+
+The calling convention works like this:
+
+
+
+
+
+The caller pushes each of the function's arguments on the stack one by
+one, normally using the PUSH assembly language instruction.
+Arguments are pushed in right-to-left order.
+
+
+The stack grows downward: each push decrements the stack pointer, then
+stores into the location it now points to, like the C expression
+*--sp = value.
+
+
+
+
+
+The caller pushes the address of its next instruction (the return
+address) on the stack and jumps to the first instruction of the callee.
+A single 80x86 instruction, CALL, does both.
+
+
+
+
+The callee executes. When it takes control, the stack pointer points to
+the return address, the first argument is just above it, the second
+argument is just above the first argument, and so on.
+
+
+
+
+If the callee has a return value, it stores it into register EAX.
+
+
+
+
+The callee returns by popping the return address from the stack and
+jumping to the location it specifies, using the 80x86 RET
+instruction.
+
+
+
+
+The caller pops the arguments off the stack.
+
+
+
+Consider a function f() that takes three int arguments.
+This diagram shows a sample stack frame as seen by the callee at the
+beginning of step 3 above, supposing that f() is invoked as
+f(1, 2, 3). The initial stack address is arbitrary:
+
+
+The Pintos C library for user programs designates _start(), in
+lib/user/entry.c, as the entry point for user programs. This
+function is a wrapper around main() that calls exit() if
+main() returns:
+
+
+The kernel must put the arguments for the initial function on the stack
+before it allows the user program to begin executing. The arguments are
+passed in the same way as the normal calling convention (see section 2.5 80x86 Calling Convention).
+
+
+
+Consider how to handle arguments for the following example command:
+/bin/ls -l foo bar.
+First, break the command into words: /bin/ls,
+-l, foo, bar. Place the words at the top of the
+stack. Order doesn't matter, because they will be referenced through
+pointers.
+
+
+
+Then, push the address of each string plus a null pointer sentinel, on
+the stack, in right-to-left order. These are the elements of
+argv. The null pointer sentinel ensures that argv[argc]
+is a null pointer, as required by the C standard. The order ensures
+that argv[0] is at the lowest virtual address. Word-aligned
+accesses are faster than unaligned accesses, so for best performance
+round the stack pointer down to a multiple of 4 before the first push.
+
+
+
+Then, push argv (the address of argv[0]) and argc,
+in that order. Finally, push a fake "return address": although the
+entry function will never return, its stack frame must have the same
+structure as any other.
+
+
+
+The table below shows the state of the stack and the relevant registers
+right before the beginning of the user program, assuming
+PHYS_BASE is 0xc0000000:
+
+
+
+
+
+
+
Address
Name
Data
Type
+
+
0xbffffffc
argv[3][...]
bar\0
char[4]
+
+
0xbffffff8
argv[2][...]
foo\0
char[4]
+
+
0xbffffff5
argv[1][...]
-l\0
char[3]
+
+
0xbfffffed
argv[0][...]
/bin/ls\0
char[8]
+
+
0xbfffffec
word-align
0
uint8_t
+
+
0xbfffffe8
argv[4]
0
char *
+
+
0xbfffffe4
argv[3]
0xbffffffc
char *
+
+
0xbfffffe0
argv[2]
0xbffffff8
char *
+
+
0xbfffffdc
argv[1]
0xbffffff5
char *
+
+
0xbfffffd8
argv[0]
0xbfffffed
char *
+
+
0xbfffffd4
argv
0xbfffffd8
char **
+
+
0xbfffffd0
argc
4
int
+
+
0xbfffffcc
return address
0
void (*) ()
+
+
+
+
+In this example, the stack pointer would be initialized to
+0xbfffffcc.
+
+
+
+As shown above, your code should start the stack at the very top of
+the user virtual address space, in the page just below virtual address
+PHYS_BASE (defined in threads/vaddr.h).
+
+
+
+You may find the non-standard hex_dump() function, declared in
+<stdio.h>, useful for debugging your argument passing code.
+Here's what it would show in the above example:
+
+
+We already know one way that the operating system
+can regain control from a user program: interrupts from timers and I/O
+devices. These are "external" interrupts, because they are caused
+by entities outside the CPU (see section A.4.3 External Interrupt Handling).
+
+
+
+The operating system also deals with software exceptions, which are
+events that occur in program code (see section A.4.2 Internal Interrupt Handling). These can be errors such as a page fault or division by
+zero. Exceptions are also the means by which a user program
+can request services ("system calls") from the operating system.
+
+
+
+In the 80x86 architecture, the int instruction is the
+most commonly used means for invoking system calls. This instruction
+is handled in the same way as other software exceptions. In Pintos,
+user programs invoke int $0x30 to make a system call. The
+system call number and any additional arguments are expected to be
+pushed on the stack in the normal fashion before invoking the
+interrupt (see section 2.5 80x86 Calling Convention).
+
+
+
+Thus, when the system call handler syscall_handler() gets control,
+the system call number is in the 32-bit word at the caller's stack
+pointer, the first argument is in the 32-bit word at the next higher
+address, and so on. The caller's stack pointer is accessible to
+syscall_handler() as the esp member of the
+struct intr_frame passed to it. (struct intr_frame is on the kernel
+stack.)
+
+
+
+The 80x86 convention for function return values is to place them
+in the EAX register. System calls that return a value can do
+so by modifying the eax member of struct intr_frame.
+
+
+
+You should try to avoid writing large amounts of repetitive code for
+implementing system calls. Each system call argument, whether an
+integer or a pointer, takes up 4 bytes on the stack. You should be able
+to take advantage of this to avoid writing much near-identical code for
+retrieving each system call's arguments from the stack.
+
+
+
+In this assignment, we give you a minimally functional thread system.
+Your job is to extend the functionality of this system to gain a
+better understanding of synchronization problems.
+
+
+
+You will be working primarily in the threads directory for
+this assignment, with some work in the devices directory on the
+side. Compilation should be done in the threads directory.
+
+
+Before you start with project 1, be sure to refresh your knowledge
+on the thread subsystem introduced in the last project
+(2.1 Understanding Threads).
+
+
+
+
+
+
+
3.2 Requirements
+
+
+
+
+
+
+
3.2.1 Design Document
+
+
+
+Before you turn in your project, you must copy the
+project 1 design document template into your source tree under the name
+pintos/src/threads/DESIGNDOC and fill it in. We recommend that
+you read the design document template before you start working on the
+project.
+
+
+
+
+
+
+
3.2.2 Priority Scheduling
+
+
+
+Implement priority scheduling in Pintos.
+When a thread is added to the ready list that has a higher priority
+than the currently running thread, the current thread should
+immediately yield the processor to the new thread. Similarly, when
+threads are waiting for a lock, semaphore, or condition variable, the
+highest priority waiting thread should be awakened first. A thread
+may raise or lower its own priority at any time, but lowering its
+priority such that it no longer has the highest priority must cause it
+to immediately yield the CPU.
+
+
+
+Thread priorities range from PRI_MIN (0) to PRI_MAX (63).
+Lower numbers correspond to lower priorities, so that priority 0
+is the lowest priority and priority 63 is the highest.
+The initial thread priority is passed as an argument to
+thread_create(). If there's no reason to choose another
+priority, use PRI_DEFAULT (31). The PRI_ macros are
+defined in threads/thread.h, and you should not change their
+values.
+
+
+
+One issue with priority scheduling is "priority inversion". Consider
+high, medium, and low priority threads H, M, and L,
+respectively. If H needs to wait for L (for instance, for a
+lock held by L), and M is on the ready list, then H
+will never get the CPU because the low priority thread will not get any
+CPU time. A partial fix for this problem is for H to "donate"
+its priority to L while L is holding the lock, then recall
+the donation once L releases (and thus H acquires) the lock.
+
+
+
+Implement priority donation. You will need to account for all different
+situations in which priority donation is required. Be sure to handle
+multiple donations, in which multiple priorities are donated to a single
+thread. You must also handle nested donation: if H is waiting on
+a lock that M holds and M is waiting on a lock that L
+holds, then both M and L should be boosted to H's
+priority. If necessary, you may impose a reasonable limit on depth of
+nested priority donation, such as 8 levels.
+
+
+
+You must implement priority donation for locks. You need not
+implement priority donation for the other Pintos synchronization
+constructs. You do need to implement priority scheduling in all
+cases.
+
+
+
+Finally, implement the following functions that allow a thread to
+examine and modify its own priority. Skeletons for these functions are
+provided in threads/thread.c.
+
Sets the current thread's priority to new_priority. If the
+current thread no longer has the highest priority, yields.
+
+
+
+
+
+
+
Function: int thread_get_priority (void)
+
Returns the current thread's priority. In the presence of priority
+donation, returns the higher (donated) priority.
+
+
+
+You need not provide any interface to allow a thread to directly modify
+other threads' priorities.
+
+
+
+The priority scheduler is not a necessary for project 2.
+
+
+
+
+
+
+
3.3 FAQ
+
+
+
+
+
+
How much code will I need to write?
+
+
+Here's a summary of our reference solution, produced by the
+diffstat program. The final row gives total lines inserted
+and deleted; a changed line counts as both an insertion and a deletion.
+
+
+
+The reference solution represents just one possible solution. Many
+other solutions are also possible and many of those differ greatly from
+the reference solution. Some excellent solutions may not modify all the
+files modified by the reference solution, and some may modify files not
+modified by the reference solution.
+
+
+Yes, strict priority scheduling can lead to starvation
+because a thread will not run if any higher-priority thread is runnable.
+The advanced scheduler introduces a mechanism for dynamically
+changing thread priorities.
+
+
+
+Strict priority scheduling is valuable in real-time systems because it
+offers the programmer more control over which jobs get processing
+time. High priorities are generally reserved for time-critical
+tasks. It's not "fair," but it addresses other concerns not
+applicable to a general-purpose operating system.
+
+
+
+
+
What thread should run after a lock has been released?
+
+
+When a lock is released, the highest priority thread waiting for that
+lock should be unblocked and put on the list of ready threads. The
+scheduler should then run the highest priority thread on the ready
+list.
+
+
+
+
+
If the highest-priority thread yields, does it continue running?
+
+
+Yes. If there is a single highest-priority thread, it continues
+running until it blocks or finishes, even if it calls
+thread_yield().
+If multiple threads have the same highest priority,
+thread_yield() should switch among them in "round robin" order.
+
+
+
+
+
What happens to the priority of a donating thread?
+
+
+Priority donation only changes the priority of the donee
+thread. The donor thread's priority is unchanged.
+Priority donation is not additive: if thread A (with priority 5) donates
+to thread B (with priority 3), then B's new priority is 5, not 8.
+
+
+
+
+
Can a thread's priority change while it is on the ready queue?
+
+
+Yes. Consider a ready, low-priority thread L that holds a lock.
+High-priority thread H attempts to acquire the lock and blocks,
+thereby donating its priority to ready thread L.
+
+
+
+
+
Can a thread's priority change while it is blocked?
+
+
+Yes. While a thread that has acquired lock L is blocked for any
+reason, its priority can increase by priority donation if a
+higher-priority thread attempts to acquire L. This case is
+checked by the priority-donate-sema test.
+
+
+
+
+
Can a thread added to the ready list preempt the processor?
+
+
+Yes. If a thread added to the ready list has higher priority than the
+running thread, the correct behavior is to immediately yield the
+processor. It is not acceptable to wait for the next timer interrupt.
+The highest priority thread should run as soon as it is runnable,
+preempting whatever thread is currently running.
+
+
+
+
+
How does thread_set_priority() affect a thread receiving donations?
+
+
+It sets the thread's base priority. The thread's effective priority
+becomes the higher of the newly set priority or the highest donated
+priority. When the donations are released, the thread's priority
+becomes the one set through the function call. This behavior is checked
+by the priority-donate-lower test.
+
+
+
+
+
Doubled test names in output make them fail.
+
+
+Suppose you are seeing output in which some test names are doubled,
+like this:
+
+
+What is happening is that output from two threads is being
+interleaved. That is, one thread is printing "(alarm-priority)
+Thread priority 29 woke up.\n" and another thread is printing
+"(alarm-priority) Thread priority 30 woke up.\n", but the first
+thread is being preempted by the second in the middle of its output.
+
+
+
+This problem indicates a bug in your priority scheduler. After all, a
+thread with priority 29 should not be able to run while a thread with
+priority 30 has work to do.
+
+
+
+Normally, the implementation of the printf() function in the
+Pintos kernel attempts to prevent such interleaved output by acquiring
+a console lock during the duration of the printf call and
+releasing it afterwards. However, the output of the test name,
+e.g., (alarm-priority), and the message following it is output
+using two calls to printf, resulting in the console lock being
+acquired and released twice.
+
+
+By now you should have some familiarity with the inner workings of
+Pintos. Pintos can properly handle multiple threads of execution with proper
+synchronization, and can load multiple user programs at once. In this assignment,
+you will improve the memory management of Pintos.
+
+
+
+This assignment requires user programs (and in particular argument passing,
+which you implemented in project 0) to work. You will continue to handle
+Pintos disks and file systems the same way you did before
+(see section 2.2.2 Using the File System).
+
+
+
+The documentation for this assignment will be released when Project 2
+is about to start.
+
+
+This chapter is a reference for the Pintos code. The reference guide
+does not cover all of the code in Pintos, but it does cover those
+pieces that students most often find troublesome. You may find that
+you want to read each part of the reference guide as you work on the
+project where it becomes important.
+
+
+
+We recommend using "tags" to follow along with references to function
+and variable names (see section E.1 Tags).
+
+
+
+
+
+
+
A.1 Loading
+
+
+
+This section covers the Pintos loader and basic kernel
+initialization.
+
+
+
+
+
+
+
A.1.1 The Loader
+
+
+
+The first part of Pintos that runs is the loader, in
+threads/loader.S. The PC BIOS loads the loader into memory.
+The loader, in turn, is responsible for finding the kernel on disk,
+loading it into memory, and then jumping to its start. It's
+not important to understand exactly how the loader works, but if
+you're interested, read on. You should probably read along with the
+loader's source. You should also understand the basics of the
+80x86 architecture as described by chapter 3, "Basic Execution
+Environment," of [ IA32-v1].
+
+
+
+The PC BIOS loads the loader from the first sector of the first hard
+disk, called the master boot record (MBR). PC conventions
+reserve 64 bytes of the MBR for the partition table, and Pintos uses
+about 128 additional bytes for kernel command-line arguments. This
+leaves a little over 300 bytes for the loader's own code. This is a
+severe restriction that means, practically speaking, the loader must
+be written in assembly language.
+
+
+
+The Pintos loader and kernel don't have to be on the same disk, nor
+does is the kernel required to be in any particular location on a
+given disk. The loader's first job, then, is to find the kernel by
+reading the partition table on each hard disk, looking for a bootable
+partition of the type used for a Pintos kernel.
+
+
+
+When the loader finds a bootable kernel partition, it reads the
+partition's contents into memory at physical address 128 kB. The
+kernel is at the beginning of the partition, which might be larger
+than necessary due to partition boundary alignment conventions, so the
+loader reads no more than 512 kB (and the Pintos build process
+will refuse to produce kernels larger than that). Reading more data
+than this would cross into the region from 640 kB to 1 MB that
+the PC architecture reserves for hardware and the BIOS, and a standard
+PC BIOS does not provide any means to load the kernel above 1 MB.
+
+
+
+The loader's final job is to extract the entry point from the loaded
+kernel image and transfer control to it. The entry point is not at a
+predictable location, but the kernel's ELF header contains a pointer
+to it. The loader extracts the pointer and jumps to the location it
+points to.
+
+
+
+The Pintos kernel command line
+is stored in the boot loader. The pintos program actually
+modifies a copy of the boot loader on disk each time it runs the kernel,
+inserting whatever command-line arguments the user supplies to the kernel,
+and then the kernel at boot time reads those arguments out of the boot
+loader in memory. This is not an elegant solution, but it is simple
+and effective.
+
+
+
+
+
+
+
A.1.2 Low-Level Kernel Initialization
+
+
+
+The loader's last action is to transfer control to the kernel's entry
+point, which is start() in threads/start.S. The job of
+this code is to switch the CPU from legacy 16-bit "real mode" into
+the 32-bit "protected mode" used by all modern 80x86 operating
+systems.
+
+
+
+The startup code's first task is actually to obtain the machine's
+memory size, by asking the BIOS for the PC's memory size. The
+simplest BIOS function to do this can only detect up to 64 MB of RAM,
+so that's the practical limit that Pintos can support. The function
+stores the memory size, in pages, in global variable
+init_ram_pages.
+
+
+
+The first part of CPU initialization is to enable the A20 line, that
+is, the CPU's address line numbered 20. For historical reasons, PCs
+boot with this address line fixed at 0, which means that attempts to
+access memory beyond the first 1 MB (2 raised to the 20th power) will
+fail. Pintos wants to access more memory than this, so we have to
+enable it.
+
+
+
+Next, the loader creates a basic page table. This page table maps
+the 64 MB at the base of virtual memory (starting at virtual address
+0) directly to the identical physical addresses. It also maps the
+same physical memory starting at virtual address
+LOADER_PHYS_BASE, which defaults to 0xc0000000 (3 GB). The
+Pintos kernel only wants the latter mapping, but there's a
+chicken-and-egg problem if we don't include the former: our current
+virtual address is roughly 0x20000, the location where the loader
+put us, and we can't jump to 0xc0020000 until we turn on the
+page table, but if we turn on the page table without jumping there,
+then we've just pulled the rug out from under ourselves.
+
+
+
+After the page table is initialized, we load the CPU's control
+registers to turn on protected mode and paging, and set up the segment
+registers. We aren't yet equipped to handle interrupts in protected
+mode, so we disable interrupts. The final step is to call main().
+
+
+
+
+
+
+
A.1.3 High-Level Kernel Initialization
+
+
+
+The kernel proper starts with the main() function. The
+main() function is written in C, as will be most of the code we
+encounter in Pintos from here on out.
+
+
+
+When main() starts, the system is in a pretty raw state. We're
+in 32-bit protected mode with paging enabled, but hardly anything else is
+ready. Thus, the main() function consists primarily of calls
+into other Pintos modules' initialization functions.
+These are usually named module_init(), where
+module is the module's name, module.c is the
+module's source code, and module.h is the module's
+header.
+
+
+
+The first step in main() is to call bss_init(), which clears
+out the kernel's "BSS", which is the traditional name for a
+segment that should be initialized to all zeros. In most C
+implementations, whenever you
+declare a variable outside a function without providing an
+initializer, that variable goes into the BSS. Because it's all zeros, the
+BSS isn't stored in the image that the loader brought into memory. We
+just use memset() to zero it out.
+
+
+
+Next, main() calls read_command_line() to break the kernel command
+line into arguments, then parse_options() to read any options at
+the beginning of the command line. (Actions specified on the
+command line execute later.)
+
+
+
+thread_init() initializes the thread system. We will defer full
+discussion to our discussion of Pintos threads below. It is called so
+early in initialization because a valid thread structure is a
+prerequisite for acquiring a lock, and lock acquisition in turn is
+important to other Pintos subsystems. Then we initialize the console
+and print a startup message to the console.
+
+
+
+The next block of functions we call initializes the kernel's memory
+system. palloc_init() sets up the kernel page allocator, which
+doles out memory one or more pages at a time (see section A.5.1 Page Allocator).
+malloc_init() sets
+up the allocator that handles allocations of arbitrary-size blocks of
+memory (see section A.5.2 Block Allocator).
+paging_init() sets up a page table for the kernel (see section A.7 Page Table).
+
+
+
+In projects 2 and later, main() also calls tss_init() and
+gdt_init().
+
+
+
+The next set of calls initializes the interrupt system.
+intr_init() sets up the CPU's interrupt descriptor table
+(IDT) to ready it for interrupt handling (see section A.4.1 Interrupt Infrastructure), then timer_init() and kbd_init() prepare for
+handling timer interrupts and keyboard interrupts, respectively.
+input_init() sets up to merge serial and keyboard input into one
+stream. In
+projects 2 and later, we also prepare to handle interrupts caused by
+user programs using exception_init() and syscall_init().
+
+
+
+Now that interrupts are set up, we can start the scheduler
+with thread_start(), which creates the idle thread and enables
+interrupts.
+With interrupts enabled, interrupt-driven serial port I/O becomes
+possible, so we use
+serial_init_queue() to switch to that mode. Finally,
+timer_calibrate() calibrates the timer for accurate short delays.
+
+
+
+If the file system is compiled in, as it will starting in project 2, we
+initialize the IDE disks with ide_init(), then the
+file system with filesys_init().
+
+
+
+Boot is complete, so we print a message.
+
+
+
+Function run_actions() now parses and executes actions specified on
+the kernel command line, such as run to run a test (in project
+1) or a user program (in later projects).
+
+
+
+Finally, if -q was specified on the kernel command line, we
+call shutdown_power_off() to terminate the machine simulator. Otherwise,
+main() calls thread_exit(), which allows any other running
+threads to continue running.
+
+
+
+
+
+
+
A.1.4 Physical Memory Map
+
+
+
+
+
+@headitem Memory Range
+
Owner
+
Contents
+
+
00000000--000003ff
CPU
Real mode interrupt table.
+
+
00000400--000005ff
BIOS
Miscellaneous data area.
+
+
00000600--00007bff
--
---
+
+
00007c00--00007dff
Pintos
Loader.
+
+
0000e000--0000efff
Pintos
+
Stack for loader; kernel stack and struct thread for initial
+kernel thread.
+
+
0000f000--0000ffff
Pintos
+
Page directory for startup code.
+
+
00010000--00020000
Pintos
+
Page tables for startup code.
+
+
00020000--0009ffff
Pintos
+
Kernel code, data, and uninitialized data segments.
+
+
000a0000--000bffff
Video
VGA display memory.
+
+
000c0000--000effff
Hardware
+
Reserved for expansion card RAM and ROM.
+
+
000f0000--000fffff
BIOS
ROM BIOS.
+
+
00100000--03ffffff
Pintos
Dynamic memory allocation.
+
+
+
+
+
+
+
A.2 Threads
+
+
+
+
+
+
+
A.2.1 struct thread
+
+
+
+The main Pintos data structure for threads is struct thread,
+declared in threads/thread.h.
+
+
+
+
+
+
+
Structure:struct thread
+
Represents a thread or a user process. In the projects, you will have
+to add your own members to struct thread. You may also change or
+delete the definitions of existing members.
+
+
+Every struct thread occupies the beginning of its own page of
+memory. The rest of the page is used for the thread's stack, which
+grows downward from the end of the page. It looks like this:
+
+
+This has two consequences. 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. The base struct thread is only a few bytes in size. It
+probably should stay well under 1 kB.
+
+
+
+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 (see section A.5 Memory Allocation).
+
+
+
+
+
+
+
+
Member of struct thread: tid_t tid
+
The thread's thread identifier or tid. Every thread must have a
+tid that is unique over the entire lifetime of the kernel. By
+default, tid_t is a typedef for int and each new
+thread receives the numerically next higher tid, starting from 1 for
+the initial process. You can change the type and the numbering scheme
+if you like.
+
+
+
+
+
+
+
Member of struct thread: enum thread_status status
+
+The thread's state, one of the following:
+
+
+
+
+
+
Thread State:THREAD_RUNNING
+
The thread is running. Exactly one thread is running at a given time.
+thread_current() returns the running thread.
+
+
+
+
+
+
+
Thread State:THREAD_READY
+
The thread is ready to run, but it's not running right now. The
+thread could be selected to run the next time the scheduler is
+invoked. Ready threads are kept in a doubly linked list called
+ready_list.
+
+
+
+
+
+
+
Thread State:THREAD_BLOCKED
+
The thread is waiting for something, e.g. a lock to become
+available, an interrupt to be invoked. The thread won't be scheduled
+again until it transitions to the THREAD_READY state with a
+call to thread_unblock(). This is most conveniently done
+indirectly, using one of the Pintos synchronization primitives that
+block and unblock threads automatically (see section A.3 Synchronization).
+
+
+There is no a priori way to tell what a blocked thread is waiting
+for, but a backtrace can help (see section D.4 Backtraces).
+
+
+
+
+
+
+
+
Thread State:THREAD_DYING
+
The thread will be destroyed by the scheduler after switching to the
+next thread.
+
+
+
+
+
+
+
+
Member of struct thread: char name[16]
+
The thread's name as a string, or at least the first few characters of
+it.
+
+
+
+
+
+
+
Member of struct thread: uint8_t *stack
+
Every thread has its own stack to keep track of its state. When the
+thread is running, the CPU's stack pointer register tracks the top of
+the stack and this member is unused. But when the CPU switches to
+another thread, this member saves the thread's stack pointer. No
+other members are needed to save the thread's registers, because the
+other registers that must be saved are saved on the stack.
+
+
+When an interrupt occurs, whether in the kernel or a user program, an
+struct intr_frame is pushed onto the stack. When the interrupt occurs
+in a user program, the struct intr_frame is always at the very top of
+the page. See section A.4 Interrupt Handling, for more information.
+
+
+
+
+
+
+
+
Member of struct thread: int priority
+
A thread priority, ranging from PRI_MIN (0) to PRI_MAX
+(63). Lower numbers correspond to lower priorities, so that
+priority 0 is the lowest priority and priority 63 is the highest.
+Pintos as provided ignores thread priorities, but you will implement
+priority scheduling in project 1 (see section 3.2.2 Priority Scheduling).
+
+
+
+
+
+
+
Member of struct thread:struct list_elemallelem
+
This "list element" is used to link the thread into the list of all
+threads. Each thread is inserted into this list when it is created
+and removed when it exits. The thread_foreach() function should
+be used to iterate over all threads.
+
+
+
+
+
+
+
Member of struct thread:struct list_elemelem
+
A "list element" used to put the thread into doubly linked lists,
+either ready_list (the list of threads ready to run) or a list of
+threads waiting on a semaphore in sema_down(). It can do double
+duty because a thread waiting on a semaphore is not ready, and vice
+versa.
+
+
+
+
+
+
+
Member of struct thread: uint32_t *pagedir
+
Only present in project 2 and later. See Page Tables.
+
+
+
+
+
+
+
Member of struct thread: unsigned magic
+
Always set to THREAD_MAGIC, which is just an arbitrary number defined
+in threads/thread.c, and used to detect stack overflow.
+thread_current() checks that the magic member of the running
+thread's struct thread is set to THREAD_MAGIC. Stack overflow
+tends to change this value, triggering the assertion. For greatest
+benefit, as you add members to struct thread, leave magic at
+the end.
+
+
+
+
+
+
+
A.2.2 Thread Functions
+
+
+
+threads/thread.c implements several public functions for thread
+support. Let's take a look at the most useful:
+
+
+
+
+
+
+
Function: void thread_init (void)
+
Called by main() to initialize the thread system. Its main
+purpose is to create a struct thread for Pintos's initial thread.
+This is possible because the Pintos loader puts the initial
+thread's stack at the top of a page, in the same position as any other
+Pintos thread.
+
+
+Before thread_init() runs,
+thread_current() will fail because the running thread's
+magic value is incorrect. Lots of functions call
+thread_current() directly or indirectly, including
+lock_acquire() for locking a lock, so thread_init() is
+called early in Pintos initialization.
+
+
+
+
+
+
+
+
Function: void thread_start (void)
+
Called by main() to start the scheduler. Creates the idle
+thread, that is, the thread that is scheduled when no other thread is
+ready. Then enables interrupts, which as a side effect enables the
+scheduler because the scheduler runs on return from the timer interrupt, using
+intr_yield_on_return() (see section A.4.3 External Interrupt Handling).
+
+
+
+
+
+
+
Function: void thread_tick (void)
+
Called by the timer interrupt at each timer tick. It keeps track of
+thread statistics and triggers the scheduler when a time slice expires.
+
+
+
+
+
+
+
Function: void thread_print_stats (void)
+
Called during Pintos shutdown to print thread statistics.
+
Creates and starts a new thread named name with the given
+priority, returning the new thread's tid. The thread executes
+func, passing aux as the function's single argument.
+
+
+thread_create() allocates a page for the thread's
+struct thread and stack and initializes its members, then it sets
+up a set of fake stack frames for it (see section A.2.3 Thread Switching). The
+thread is initialized in the blocked state, then unblocked just before
+returning, which allows the new thread to
+be scheduled (see Thread States).
+
+
+
+
+
+
+
Type:void thread_func (void *aux)
+
This is the type of the function passed to thread_create(), whose
+aux argument is passed along as the function's argument.
+
+
+
+
+
+
+
+
Function: void thread_block (void)
+
Transitions the running thread from the running state to the blocked
+state (see Thread States). The thread will not run again until
+thread_unblock() is
+called on it, so you'd better have some way arranged for that to happen.
+Because thread_block() is so low-level, you should prefer to use
+one of the synchronization primitives instead (see section A.3 Synchronization).
+
Transitions thread, which must be in the blocked state, to the
+ready state, allowing it to resume running (see Thread States).
+This is called when the event that the thread is waiting for occurs,
+e.g. when the lock that
+the thread is waiting on becomes available.
+
+
+
+
+
+
+
Function: struct thread *thread_current (void)
+
Returns the running thread.
+
+
+
+
+
+
+
Function: tid_t thread_tid (void)
+
Returns the running thread's thread id. Equivalent to
+thread_current ()->tid.
+
+
+
+
+
+
+
Function: const char *thread_name (void)
+
Returns the running thread's name. Equivalent to thread_current
+()->name.
+
Yields the CPU to the scheduler, which picks a new thread to run. The
+new thread might be the current thread, so you can't depend on this
+function to keep this thread from running for any particular length of
+time.
+
Iterates over all threads t and invokes action(t, aux) on each.
+action must refer to a function that matches the signature
+given by thread_action_func():
+
Stubs for the advanced scheduler (not used in this course).
+
+
+
+
+
+
+
A.2.3 Thread Switching
+
+
+
+schedule() is responsible for switching threads. It
+is internal to threads/thread.c and called only by the three
+public thread functions that need to switch threads:
+thread_block(), thread_exit(), and thread_yield().
+Before any of these functions call schedule(), they disable
+interrupts (or ensure that they are already disabled) and then change
+the running thread's state to something other than running.
+
+
+
+schedule() is short but tricky. It records the
+current thread in local variable cur, determines the next thread
+to run as local variable next (by calling
+next_thread_to_run()), and then calls switch_threads() to do
+the actual thread switch. The thread we switched to was also running
+inside switch_threads(), as are all the threads not currently
+running, so the new thread now returns out of
+switch_threads(), returning the previously running thread.
+
+
+
+switch_threads() is an assembly language routine in
+threads/switch.S. It saves registers on the stack, saves the
+CPU's current stack pointer in the current struct thread's stack
+member, restores the new thread's stack into the CPU's stack
+pointer, restores registers from the stack, and returns.
+
+
+
+The rest of the scheduler is implemented in thread_schedule_tail(). It
+marks the new thread as running. If the thread we just switched from
+is in the dying state, then it also frees the page that contained the
+dying thread's struct thread and stack. These couldn't be freed
+prior to the thread switch because the switch needed to use it.
+
+
+
+Running a thread for the first time is a special case. When
+thread_create() creates a new thread, it goes through a fair
+amount of trouble to get it started properly. In particular, the new
+thread hasn't started running yet, so there's no way for it to be
+running inside switch_threads() as the scheduler expects. To
+solve the problem, thread_create() creates some fake stack frames
+in the new thread's stack:
+
+
+
+
+
+The topmost fake stack frame is for switch_threads(), represented
+by struct switch_threads_frame. The important part of this frame is
+its eip member, the return address. We point eip to
+switch_entry(), indicating it to be the function that called
+switch_entry().
+
+
+
+
+The next fake stack frame is for switch_entry(), an assembly
+language routine in threads/switch.S that adjusts the stack
+pointer,(3)
+calls thread_schedule_tail() (this special case is why
+thread_schedule_tail() is separate from schedule()), and returns.
+We fill in its stack frame so that it returns into
+kernel_thread(), a function in threads/thread.c.
+
+
+
+
+The final stack frame is for kernel_thread(), which enables
+interrupts and calls the thread's function (the function passed to
+thread_create()). If the thread's function returns, it calls
+thread_exit() to terminate the thread.
+
+
+
+
+
+
+
A.3 Synchronization
+
+
+
+If sharing of resources between threads is not handled in a careful,
+controlled fashion, the result is usually a big mess.
+This is especially the case in operating system kernels, where
+faulty sharing can crash the entire machine. Pintos provides several
+synchronization primitives to help out.
+
+
+
+
+
+
+
A.3.1 Disabling Interrupts
+
+
+
+The crudest way to do synchronization is to disable interrupts, that
+is, to temporarily prevent the CPU from responding to interrupts. If
+interrupts are off, no other thread will preempt the running thread,
+because thread preemption is driven by the timer interrupt. If
+interrupts are on, as they normally are, then the running thread may
+be preempted by another at any time, whether between two C statements
+or even within the execution of one.
+
+
+
+Incidentally, this means that Pintos is a "preemptible kernel," that
+is, kernel threads can be preempted at any time. Traditional Unix
+systems are "nonpreemptible," that is, kernel threads can only be
+preempted at points where they explicitly call into the scheduler.
+(User programs can be preempted at any time in both models.) As you
+might imagine, preemptible kernels require more explicit
+synchronization.
+
+
+
+You should have little need to set the interrupt state directly. Most
+of the time you should use the other synchronization primitives
+described in the following sections. The main reason to disable
+interrupts is to synchronize kernel threads with external interrupt
+handlers, which cannot sleep and thus cannot use most other forms of
+synchronization (see section A.4.3 External Interrupt Handling).
+
+
+
+Some external interrupts cannot be postponed, even by disabling
+interrupts. These interrupts, called non-maskable interrupts
+(NMIs), are supposed to be used only in emergencies, e.g. when the
+computer is on fire. Pintos does not handle non-maskable interrupts.
+
+
+
+Types and functions for disabling and enabling interrupts are in
+threads/interrupt.h.
+
+
+
+
+
+
+
Type:enum intr_level
+
One of INTR_OFF or INTR_ON, denoting that interrupts are
+disabled or enabled, respectively.
+
Turns interrupts on or off according to level. Returns the
+previous interrupt state.
+
+
+
+
+
+
+
Function: enum intr_level intr_enable (void)
+
Turns interrupts on. Returns the previous interrupt state.
+
+
+
+
+
+
+
Function: enum intr_level intr_disable (void)
+
Turns interrupts off. Returns the previous interrupt state.
+
+
+
+
+
+
+
A.3.2 Semaphores
+
+
+
+A semaphore is a nonnegative integer together with two operators
+that manipulate it atomically, which are:
+
+
+
+
+
+"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).
+
+
+
+A semaphore initialized to 0 may be used to wait for an event
+that will happen exactly once. For example, suppose thread A
+starts another thread B and wants to wait for B to signal
+that some activity is complete. A can create a semaphore
+initialized to 0, pass it to B as it starts it, and then
+"down" the semaphore. When B finishes its activity, it
+"ups" the semaphore. This works regardless of whether A
+"downs" the semaphore or B "ups" it first.
+
+
+
+A semaphore initialized to 1 is typically used for controlling access
+to a resource. Before a block of code starts using the resource, it
+"downs" the semaphore, then after it is done with the resource it
+"ups" the resource. In such a case a lock, described below, may be
+more appropriate.
+
+
+
+Semaphores can also be initialized to values larger than 1. These are
+rarely used.
+
+
+
+Semaphores were invented by Edsger Dijkstra and first used in the THE
+operating system ([ Dijkstra]).
+
+
+
+Pintos' semaphore type and operations are declared in
+threads/synch.h.
+
Tries to execute the "down" or "P" operation on sema,
+without waiting. Returns true if sema
+was successfully decremented, or false if it was already
+zero and thus could not be decremented without waiting. Calling this
+function in a
+tight loop wastes CPU time, so use sema_down() or find a
+different approach instead.
+
+
+
+
+
+
+
Function: void sema_up (struct semaphore *sema)
+
Executes the "up" or "V" operation on sema,
+incrementing its value. If any threads are waiting on
+sema, wakes one of them up.
+
+
+Unlike most synchronization primitives, sema_up() may be called
+inside an external interrupt handler (see section A.4.3 External Interrupt Handling).
+
+
+
+
+Semaphores are internally built out of disabling interrupt
+(see section A.3.1 Disabling Interrupts) and thread blocking and unblocking
+(thread_block() and thread_unblock()). Each semaphore maintains
+a list of waiting threads, using the linked list
+implementation in lib/kernel/list.c.
+
+
+
+
+
+
+
A.3.3 Locks
+
+
+
+A lock is like a semaphore with an initial value of 1
+(see section A.3.2 Semaphores). A lock's equivalent of "up" is called
+"release", and the "down" operation is called "acquire".
+
+
+
+Compared to a semaphore, a lock has one added restriction: only the
+thread that acquires a lock, called the lock's "owner", is allowed to
+release it. If this restriction is a problem, it's a good sign that a
+semaphore should be used, instead of a lock.
+
+
+
+Locks in Pintos are not "recursive," that is, it is an error for the
+thread currently holding a lock to try to acquire that lock.
+
+
+
+Lock types and functions are declared in threads/synch.h.
+
+
+
+
+
+
+
Type:struct lock
+
Represents a lock.
+
+
+
+
+
+
+
Function: void lock_init (struct lock *lock)
+
Initializes lock as a new lock.
+The lock is not initially owned by any thread.
+
+
+
+
+
+
+
Function: void lock_acquire (struct lock *lock)
+
Acquires lock for the current thread, first waiting for
+any current owner to release it if necessary.
+
Tries to acquire lock for use by the current thread, without
+waiting. Returns true if successful, false if the lock is already
+owned. Calling this function in a tight loop is a bad idea because it
+wastes CPU time, so use lock_acquire() instead.
+
+
+
+
+
+
+
Function: void lock_release (struct lock *lock)
+
Releases lock, which the current thread must own.
+
Returns true if the running thread owns lock,
+false otherwise.
+There is no function to test whether an arbitrary thread owns a lock,
+because the answer could change before the caller could act on it.
+
+
+
+
+
+
+
A.3.4 Monitors
+
+
+
+A monitor is a higher-level form of synchronization than a
+semaphore or a lock. A monitor consists of data being synchronized,
+plus a lock, called the monitor lock, and one or more
+condition variables. Before it accesses the protected data, a
+thread first acquires the monitor lock. It is then said to be "in the
+monitor". While in the monitor, the thread has control over all the
+protected data, which it may freely examine or modify. When access to
+the protected data is complete, it releases the monitor lock.
+
+
+
+Condition variables allow code in the monitor to wait for a condition to
+become true. Each condition variable is associated with an abstract
+condition, e.g. "some data has arrived for processing" or "over 10
+seconds has passed since the user's last keystroke". When code in the
+monitor needs to wait for a condition to become true, it "waits" on
+the associated condition variable, which releases the lock and waits for
+the condition to be signaled. If, on the other hand, it has caused one
+of these conditions to become true, it "signals" the condition to wake
+up one waiter, or "broadcasts" the condition to wake all of them.
+
+
+
+The theoretical framework for monitors was laid out by C. A. R.
+Hoare ([ Hoare]). Their practical usage was later elaborated in a
+paper on the Mesa operating system ([ Lampson]).
+
+
+
+Condition variable types and functions are declared in
+threads/synch.h.
+
Atomically releases lock (the monitor lock) and waits for
+cond to be signaled by some other piece of code. After
+cond is signaled, reacquires lock before returning.
+lock must be held before calling this function.
+
+
+Sending a signal and waking up from a wait are not an atomic operation.
+Thus, typically cond_wait()'s caller must recheck the condition
+after the wait completes and, if necessary, wait again. See the next
+section for an example.
+
If any threads are waiting on cond (protected by monitor lock
+lock), then this function wakes up one of them. If no threads are
+waiting, returns without performing any action.
+lock must be held before calling this function.
+
Wakes up all threads, if any, waiting on cond (protected by
+monitor lock lock). lock must be held before calling this
+function.
+
+
+
+
+
+
A.3.4.1 Monitor Example
+
+
+
+The classical example of a monitor is handling a buffer into which one
+or more
+"producer" threads write characters and out of which one or more
+"consumer" threads read characters. To implement this we need,
+besides the monitor lock, two condition variables which we will call
+not_full and not_empty:
+
+
+
+
char buf[BUF_SIZE]; /* Buffer. */
+size_t n = 0; /* 0 <= n <= BUF_SIZE: # of characters in buffer. */
+size_t head = 0; /* buf index of next char to write (mod BUF_SIZE). */
+size_t tail = 0; /* buf index of next char to read (mod BUF_SIZE). */
+struct lock lock; /* Monitor lock. */
+struct condition not_empty; /* Signaled when the buffer is not empty. */
+struct condition not_full; /* Signaled when the buffer is not full. */
+
+...initialize the locks and condition variables...
+
+void put (char ch) {
+ lock_acquire (&lock);
+ while (n == BUF_SIZE) /* Can't add to buf as long as it's full. */
+ cond_wait (¬_full, &lock);
+ buf[head++ % BUF_SIZE] = ch; /* Add ch to buf. */
+ n++;
+ cond_signal (¬_empty, &lock); /* buf can't be empty anymore. */
+ lock_release (&lock);
+}
+
+char get (void) {
+ char ch;
+ lock_acquire (&lock);
+ while (n == 0) /* Can't read buf as long as it's empty. */
+ cond_wait (¬_empty, &lock);
+ ch = buf[tail++ % BUF_SIZE]; /* Get ch from buf. */
+ n--;
+ cond_signal (¬_full, &lock); /* buf can't be full anymore. */
+ lock_release (&lock);
+}
+
+
+Note that BUF_SIZE must divide evenly into SIZE_MAX + 1
+for the above code to be completely correct. Otherwise, it will fail
+the first time head wraps around to 0. In practice,
+BUF_SIZE would ordinarily be a power of 2.
+
+
+
+
+
+
+
A.3.5 Optimization Barriers
+
+
+
+An optimization barrier is a special statement that prevents the
+compiler from making assumptions about the state of memory across the
+barrier. The compiler will not reorder reads or writes of variables
+across the barrier or assume that a variable's value is unmodified
+across the barrier, except for local variables whose address is never
+taken. In Pintos, threads/synch.h defines the barrier()
+macro as an optimization barrier.
+
+
+
+One reason to use an optimization barrier is when data can change
+asynchronously, without the compiler's knowledge, e.g. by another
+thread or an interrupt handler. The too_many_loops() function in
+devices/timer.c is an example. This function starts out by
+busy-waiting in a loop until a timer tick occurs:
+
+
+
+
/* Wait for a timer tick. */
+int64_t start = ticks;
+while (ticks == start)
+ barrier ();
+
+
+Without an optimization barrier in the loop, the compiler could
+conclude that the loop would never terminate, because start and
+ticks start out equal and the loop itself never changes them.
+It could then "optimize" the function into an infinite loop, which
+would definitely be undesirable.
+
+
+
+Optimization barriers can be used to avoid other compiler
+optimizations. The busy_wait() function, also in
+devices/timer.c, is an example. It contains this loop:
+
+
+
+
while (loops-- > 0)
+ barrier ();
+
+
+The goal of this loop is to busy-wait by counting loops down
+from its original value to 0. Without the barrier, the compiler could
+delete the loop entirely, because it produces no useful output and has
+no side effects. The barrier forces the compiler to pretend that the
+loop body has an important effect.
+
+
+
+Finally, optimization barriers can be used to force the ordering of
+memory reads or writes. For example, suppose we add a "feature"
+that, whenever a timer interrupt occurs, the character in global
+variable timer_put_char is printed on the console, but only if
+global Boolean variable timer_do_put is true. The best way to
+set up x to be printed is then to use an optimization barrier,
+like this:
+
+
+Without the barrier, the code is buggy because the compiler is free to
+reorder operations when it doesn't see a reason to keep them in the
+same order. In this case, the compiler doesn't know that the order of
+assignments is important, so its optimizer is permitted to exchange
+their order. There's no telling whether it will actually do this, and
+it is possible that passing the compiler different optimization flags
+or using a different version of the compiler will produce different
+behavior.
+
+
+
+Another solution is to disable interrupts around the assignments.
+This does not prevent reordering, but it prevents the interrupt
+handler from intervening between the assignments. It also has the
+extra runtime cost of disabling and re-enabling interrupts:
+
+
+A second solution is to mark the declarations of
+timer_put_char and timer_do_put as volatile. This
+keyword tells the compiler that the variables are externally observable
+and restricts its latitude for optimization. However, the semantics of
+volatile are not well-defined, so it is not a good general
+solution. The base Pintos code does not use volatile at all.
+
+
+
+The following is not a solution, because locks neither prevent
+interrupts nor prevent the compiler from reordering the code within the
+region where the lock is held:
+
+
+The compiler treats invocation of any function defined externally,
+that is, in another source file, as a limited form of optimization
+barrier. Specifically, the compiler assumes that any externally
+defined function may access any statically or dynamically allocated
+data and any local variable whose address is taken. This often means
+that explicit barriers can be omitted. It is one reason that Pintos
+contains few explicit barriers.
+
+
+
+A function defined in the same source file, or in a header included by
+the source file, cannot be relied upon as a optimization barrier.
+This applies even to invocation of a function before its
+definition, because the compiler may read and parse the entire source
+file before performing optimization.
+
+
+
+
+
+
+
A.4 Interrupt Handling
+
+
+
+An interrupt notifies the CPU of some event. Much of the work
+of an operating system relates to interrupts in one way or another.
+For our purposes, we classify interrupts into two broad categories:
+
+
+
+
+
+Internal interrupts, that is, interrupts caused directly by CPU
+instructions. System calls, attempts at invalid memory access
+(page faults), and attempts to divide by zero are some activities
+that cause internal interrupts. Because they are caused by CPU
+instructions, internal interrupts are synchronous or synchronized
+with CPU instructions. intr_disable() does not disable internal
+interrupts.
+
+
+
+
+External interrupts, that is, interrupts originating outside the
+CPU. These interrupts come from hardware devices such as the system
+timer, keyboard, serial ports, and disks. External interrupts are
+asynchronous, meaning that their delivery is not
+synchronized with instruction execution. Handling of external interrupts
+can be postponed with intr_disable() and related functions
+(see section A.3.1 Disabling Interrupts).
+
+
+
+The CPU treats both classes of interrupts largely the same way,
+so Pintos has common infrastructure to handle both classes.
+The following section describes this
+common infrastructure. The sections after that give the specifics of
+external and internal interrupts.
+
+
+
+If you haven't already read chapter 3, "Basic Execution Environment,"
+in [ IA32-v1], it is recommended that you do so now. You might
+also want to skim chapter 5, "Interrupt and Exception Handling," in
+[ IA32-v3a].
+
+
+
+
+
+
+
A.4.1 Interrupt Infrastructure
+
+
+
+When an interrupt occurs, the CPU saves
+its most essential state on a stack and jumps to an interrupt
+handler routine. The 80x86 architecture supports 256
+interrupts, numbered 0 through 255, each with an independent
+handler defined in an array called the interrupt
+descriptor table or IDT.
+
+
+
+In Pintos, intr_init() in threads/interrupt.c sets up the
+IDT so that each entry points to a unique entry point in
+threads/intr-stubs.S named intrNN_stub(), where
+NN is the interrupt number in
+hexadecimal. Because the CPU doesn't give
+us any other way to find out the interrupt number, this entry point
+pushes the interrupt number on the stack. Then it jumps to
+intr_entry(), which pushes all the registers that the processor
+didn't already push for us, and then calls intr_handler(), which
+brings us back into C in threads/interrupt.c.
+
+
+
+The main job of intr_handler() is to call the function
+registered for handling the particular interrupt. (If no
+function is registered, it dumps some information to the console and
+panics.) It also does some extra processing for external
+interrupts (see section A.4.3 External Interrupt Handling).
+
+
+
+When intr_handler() returns, the assembly code in
+threads/intr-stubs.S restores all the CPU registers saved
+earlier and directs the CPU to return from the interrupt.
+
+
+
+The following types and functions are common to all
+interrupts.
+
This is how an interrupt handler function must be declared. Its frame
+argument (see below) allows it to determine the cause of the interrupt
+and the state of the thread that was interrupted.
+
+
+
+
+
+
+
Type:struct intr_frame
+
The stack frame of an interrupt handler, as saved by the CPU, the interrupt
+stubs, and intr_entry(). Its most interesting members are described
+below.
+
+
+
+
+
+
+
Member of struct intr_frame: uint32_t edi
+
+
Member of struct intr_frame: uint32_t esi
+
+
Member of struct intr_frame: uint32_t ebp
+
+
Member of struct intr_frame: uint32_t esp_dummy
+
+
Member of struct intr_frame: uint32_t ebx
+
+
Member of struct intr_frame: uint32_t edx
+
+
Member of struct intr_frame: uint32_t ecx
+
+
Member of struct intr_frame: uint32_t eax
+
+
Member of struct intr_frame: uint16_t es
+
+
Member of struct intr_frame: uint16_t ds
+
Register values in the interrupted thread, pushed by intr_entry().
+The esp_dummy value isn't actually used (refer to the
+description of PUSHA in [ IA32-v2b] for details).
+
+
+
+
+
+
+
Member of struct intr_frame: uint32_t vec_no
+
The interrupt vector number, ranging from 0 to 255.
+
+
+
+
+
+
+
Member of struct intr_frame: uint32_t error_code
+
The "error code" pushed on the stack by the CPU for some internal
+interrupts.
+
+
+
+
+
+
+
Member of struct intr_frame: void (*eip) (void)
+
The address of the next instruction to be executed by the interrupted
+thread.
+
+
+
+
+
+
+
Member of struct intr_frame: void *esp
+
The interrupted thread's stack pointer.
+
+
+
+
+
+
+
Function: const char *intr_name (uint8_t vec)
+
Returns the name of the interrupt numbered vec, or
+"unknown" if the interrupt has no registered name.
+
+
+
+
+
+
+
A.4.2 Internal Interrupt Handling
+
+
+
+Internal interrupts are caused directly by CPU instructions executed by
+the running kernel thread or user process (from project 2 onward). An
+internal interrupt is therefore said to arise in a "process context."
+
+
+
+In an internal interrupt's handler, it can make sense to examine the
+struct intr_frame passed to the interrupt handler, or even to modify
+it. When the interrupt returns, modifications in struct intr_frame
+become changes to the calling thread or process's state. For example,
+the Pintos system call handler returns a value to the user program by
+modifying the saved EAX register (see section 2.5.2 System Call Details).
+
+
+
+There are no special restrictions on what an internal interrupt
+handler can or can't do. Generally they should run with interrupts
+enabled, just like other code, and so they can be preempted by other
+kernel threads. Thus, they do need to synchronize with other threads
+on shared data and other resources (see section A.3 Synchronization).
+
+
+
+Internal interrupt handlers can be invoked recursively. For example,
+the system call handler might cause a page fault while attempting to
+read user memory. Deep recursion would risk overflowing the limited
+kernel stack (see section A.2.1 struct thread), but should be unnecessary.
+
Registers handler to be called when internal interrupt numbered
+vec is triggered. Names the interrupt name for debugging
+purposes.
+
+
+If level is INTR_ON, external interrupts will be processed
+normally during the interrupt handler's execution, which is normally
+desirable. Specifying INTR_OFF will cause the CPU to disable
+external interrupts when it invokes the interrupt handler. The effect
+is slightly different from calling intr_disable() inside the
+handler, because that leaves a window of one or more CPU instructions in
+which external interrupts are still enabled. This is important for the
+page fault handler; refer to the comments in userprog/exception.c
+for details.
+
+
+
+dpl determines how the interrupt can be invoked. If dpl is
+0, then the interrupt can be invoked only by kernel threads. Otherwise
+dpl should be 3, which allows user processes to invoke the
+interrupt with an explicit INT instruction. The value of dpl
+doesn't affect user processes' ability to invoke the interrupt
+indirectly, e.g. an invalid memory reference will cause a page fault
+regardless of dpl.
+
+
+
+
+
+
+
+
A.4.3 External Interrupt Handling
+
+
+
+External interrupts are caused by events outside the CPU.
+They are asynchronous, so they can be invoked at any time that
+interrupts have not been disabled. We say that an external interrupt
+runs in an "interrupt context."
+
+
+
+In an external interrupt, the struct intr_frame passed to the
+handler is not very meaningful. It describes the state of the thread
+or process that was interrupted, but there is no way to predict which
+one that is. It is possible, although rarely useful, to examine it, but
+modifying it is a recipe for disaster.
+
+
+
+Only one external interrupt may be processed at a time. Neither
+internal nor external interrupt may nest within an external interrupt
+handler. Thus, an external interrupt's handler must run with interrupts
+disabled (see section A.3.1 Disabling Interrupts).
+
+
+
+An external interrupt handler must not sleep or yield, which rules out
+calling lock_acquire(), thread_yield(), and many other
+functions. Sleeping in interrupt context would effectively put the
+interrupted thread to sleep, too, until the interrupt handler was again
+scheduled and returned. This would be unfair to the unlucky thread, and
+it would deadlock if the handler were waiting for the sleeping thread
+to, e.g., release a lock.
+
+
+
+An external interrupt handler
+effectively monopolizes the machine and delays all other activities.
+Therefore, external interrupt handlers should complete as quickly as
+they can. Anything that require much CPU time should instead run in a
+kernel thread, possibly one that the interrupt triggers using a
+synchronization primitive.
+
+
+
+External interrupts are controlled by a
+pair of devices outside the CPU called programmable interrupt
+controllers, PICs for short. When intr_init() sets up the
+CPU's IDT, it also initializes the PICs for interrupt handling. The
+PICs also must be "acknowledged" at the end of processing for each
+external interrupt. intr_handler() takes care of that by calling
+pic_end_of_interrupt(), which properly signals the PICs.
+
+
+
+The following functions relate to external
+interrupts.
+
Registers handler to be called when external interrupt numbered
+vec is triggered. Names the interrupt name for debugging
+purposes. The handler will run with interrupts disabled.
+
+
+
+
+
+
+
Function: bool intr_context (void)
+
Returns true if we are running in an interrupt context, otherwise
+false. Mainly used in functions that might sleep
+or that otherwise should not be called from interrupt context, in this
+form:
+
ASSERT (!intr_context ());
+
+
+
+
+
+
+
Function: void intr_yield_on_return (void)
+
When called in an interrupt context, causes thread_yield() to be
+called just before the interrupt returns. Used
+in the timer interrupt handler when a thread's time slice expires, to
+cause a new thread to be scheduled.
+
+
+
+
+
+
+
A.5 Memory Allocation
+
+
+
+Pintos contains two memory allocators, one that allocates memory in
+units of a page, and one that can allocate blocks of any size.
+
+
+
+
+
+
+
A.5.1 Page Allocator
+
+
+
+The page allocator declared in threads/palloc.h allocates
+memory in units of a page. It is most often used to allocate memory
+one page at a time, but it can also allocate multiple contiguous pages
+at once.
+
+
+
+The page allocator divides the memory it allocates into two pools,
+called the kernel and user pools. By default, each pool gets half of
+system memory above 1 MB, but the division can be changed with the
+-ul kernel
+command line
+option (see Why PAL_USER?). An allocation request draws from one
+pool or the other. If one pool becomes empty, the other may still
+have free pages. The user pool should be used for allocating memory
+for user processes and the kernel pool for all other allocations.
+This will only become important starting with project 3. Until then,
+all allocations should be made from the kernel pool.
+
+
+
+Each pool's usage is tracked with a bitmap, one bit per page in
+the pool. A request to allocate n pages scans the bitmap
+for n consecutive bits set to
+false, indicating that those pages are free, and then sets those bits
+to true to mark them as used. This is a "first fit" allocation
+strategy (see Wilson).
+
+
+
+The page allocator is subject to fragmentation. That is, it may not
+be possible to allocate n contiguous pages even though n
+or more pages are free, because the free pages are separated by used
+pages. In fact, in pathological cases it may be impossible to
+allocate 2 contiguous pages even though half of the pool's pages are free.
+Single-page requests can't fail due to fragmentation, so
+requests for multiple contiguous pages should be limited as much as
+possible.
+
+
+
+Pages may not be allocated from interrupt context, but they may be
+freed.
+
+
+
+When a page is freed, all of its bytes are cleared to 0xcc, as
+a debugging aid (see section D.8 Tips).
+
+
+
+Page allocator types and functions are described below.
+
Obtains and returns one page, or page_cnt contiguous pages,
+respectively. Returns a null pointer if the pages cannot be allocated.
+
+
+The flags argument may be any combination of the following flags:
+
+
+
+
+
+
+
Page Allocator Flag:PAL_ASSERT
+
If the pages cannot be allocated, panic the kernel. This is only
+appropriate during kernel initialization. User processes
+should never be permitted to panic the kernel.
+
+
+
+
+
+
+
Page Allocator Flag:PAL_ZERO
+
Zero all the bytes in the allocated pages before returning them. If not
+set, the contents of newly allocated pages are unpredictable.
+
+
+
+
+
+
+
Page Allocator Flag:PAL_USER
+
Obtain the pages from the user pool. If not set, pages are allocated
+from the kernel pool.
+
Frees one page, or page_cnt contiguous pages, respectively,
+starting at pages. All of the pages must have been obtained using
+palloc_get_page() or palloc_get_multiple().
+
+
+
+
+
+
+
A.5.2 Block Allocator
+
+
+
+The block allocator, declared in threads/malloc.h, can allocate
+blocks of any size. It is layered on top of the page allocator
+described in the previous section. Blocks returned by the block
+allocator are obtained from the kernel pool.
+
+
+
+The block allocator uses two different strategies for allocating memory.
+The first strategy applies to blocks that are 1 kB or smaller
+(one-fourth of the page size). These allocations are rounded up to the
+nearest power of 2, or 16 bytes, whichever is larger. Then they are
+grouped into a page used only for allocations of that size.
+
+
+
+The second strategy applies to blocks larger than 1 kB.
+These allocations (plus a small amount of overhead) are rounded up to
+the nearest page in size, and then the block allocator requests that
+number of contiguous pages from the page allocator.
+
+
+
+In either case, the difference between the allocation requested size
+and the actual block size is wasted. A real operating system would
+carefully tune its allocator to minimize this waste, but this is
+unimportant in an instructional system like Pintos.
+
+
+
+As long as a page can be obtained from the page allocator, small
+allocations always succeed. Most small allocations do not require a
+new page from the page allocator at all, because they are satisfied
+using part of a page already allocated. However, large allocations
+always require calling into the page allocator, and any allocation
+that needs more than one contiguous page can fail due to fragmentation,
+as already discussed in the previous section. Thus, you should
+minimize the number of large allocations in your code, especially
+those over approximately 4 kB each.
+
+
+
+When a block is freed, all of its bytes are cleared to 0xcc, as
+a debugging aid (see section D.8 Tips).
+
+
+
+The block allocator may not be called from interrupt context.
+
+
+
+The block allocator functions are described below. Their interfaces are
+the same as the standard C library functions of the same names.
+
+
+
+
+
+
+
Function: void *malloc (size_t size)
+
Obtains and returns a new block, from the kernel pool, at least
+size bytes long. Returns a null pointer if size is zero or
+if memory is not available.
+
+
+
+
+
+
+
Function: void *calloc (size_t a, size_t b)
+
Obtains a returns a new block, from the kernel pool, at least
+a * b bytes long. The block's contents will be
+cleared to zeros. Returns a null pointer if a or b is zero
+or if insufficient memory is available.
+
Attempts to resize block to new_size bytes, possibly moving
+it in the process. If successful, returns the new block, in which case
+the old block must no longer be accessed. On failure, returns a null
+pointer, and the old block remains valid.
+
+
+A call with block null is equivalent to malloc(). A call
+with new_size zero is equivalent to free().
+
+
+
+
+
+
+
+
Function: void free (void *block)
+
Frees block, which must have been previously returned by
+malloc(), calloc(), or realloc() (and not yet freed).
+
+
+
+
+
+
+
A.6 Virtual Addresses
+
+
+
+A 32-bit virtual address can be divided into a 20-bit page number
+and a 12-bit page offset (or just offset), like this:
+
+
+Header threads/vaddr.h defines these functions and macros for
+working with virtual addresses:
+
+
+
+
+
+
+
Macro:PGSHIFT
+
+
Macro:PGBITS
+
The bit index (0) and number of bits (12) of the offset part of a
+virtual address, respectively.
+
+
+
+
+
+
+
Macro:PGMASK
+
A bit mask with the bits in the page offset set to 1, the rest set to 0
+(0xfff).
+
+
+
+
+
+
+
Macro:PGSIZE
+
The page size in bytes (4,096).
+
+
+
+
+
+
+
Function: unsigned pg_ofs (const void *va)
+
Extracts and returns the page offset in virtual address va.
+
+
+
+
+
+
+
Function: uintptr_t pg_no (const void *va)
+
Extracts and returns the page number in virtual address va.
+
+
+
+
+
+
+
Function: void *pg_round_down (const void *va)
+
Returns the start of the virtual page that va points within, that
+is, va with the page offset set to 0.
+
+
+
+
+
+
+
Function: void *pg_round_up (const void *va)
+
Returns va rounded up to the nearest page boundary.
+
+
+
+Virtual memory in Pintos is divided into two regions: user virtual
+memory and kernel virtual memory (see section 2.2.4 Virtual Memory Layout). The
+boundary between them is PHYS_BASE:
+
+
+
+
+
+
+
Macro:PHYS_BASE
+
Base address of kernel virtual memory. It defaults to 0xc0000000 (3
+GB), but it may be changed to any multiple of 0x10000000 from
+0x80000000 to 0xf0000000.
+
+
+User virtual memory ranges from virtual address 0 up to
+PHYS_BASE. Kernel virtual memory occupies the rest of the
+virtual address space, from PHYS_BASE up to 4 GB.
+
+
+
+
+
+
+
+
Function: bool is_user_vaddr (const void *va)
+
+
Function: bool is_kernel_vaddr (const void *va)
+
Returns true if va is a user or kernel virtual address,
+respectively, false otherwise.
+
+
+
+The 80x86 doesn't provide any way to directly access memory given
+a physical address. This ability is often necessary in an operating
+system kernel, so Pintos works around it by mapping kernel virtual
+memory one-to-one to physical memory. That is, virtual address
+PHYS_BASE accesses physical address 0, virtual address
+PHYS_BASE + 0x1234 accesses physical address 0x1234, and
+so on up to the size of the machine's physical memory. Thus, adding
+PHYS_BASE to a physical address obtains a kernel virtual address
+that accesses that address; conversely, subtracting PHYS_BASE
+from a kernel virtual address obtains the corresponding physical
+address. Header threads/vaddr.h provides a pair of functions to
+do these translations:
+
+
+
+
+
+
+
Function: void *ptov (uintptr_t pa)
+
Returns the kernel virtual address corresponding to physical address
+pa, which should be between 0 and the number of bytes of physical
+memory.
+
+
+
+
+
+
+
Function: uintptr_t vtop (void *va)
+
Returns the physical address corresponding to va, which must be a
+kernel virtual address.
+
+
+
+
+
+
+
A.7 Page Table
+
+
+
+The code in pagedir.c is an abstract interface to the 80x86
+hardware page table, also called a "page directory" by Intel processor
+documentation. The page table interface uses a uint32_t * to
+represent a page table because this is convenient for accessing their
+internal structure.
+
+
+
+The sections below describe the page table interface and internals.
+
+
+
+
+
+
+
A.7.1 Creation, Destruction, and Activation
+
+
+
+These functions create, destroy, and activate page tables. The base
+Pintos code already calls these functions where necessary, so it should
+not be necessary to call them yourself.
+
+
+
+
+
+
+
Function: uint32_t *pagedir_create (void)
+
Creates and returns a new page table. The new page table contains
+Pintos's normal kernel virtual page mappings, but no user virtual
+mappings.
+
+
+Returns a null pointer if memory cannot be obtained.
+
+
+
+
+
+
+
+
Function: void pagedir_destroy (uint32_t *pd)
+
Frees all of the resources held by pd, including the page table
+itself and the frames that it maps.
+
+
+
+
+
+
+
Function: void pagedir_activate (uint32_t *pd)
+
Activates pd. The active page table is the one used by the CPU to
+translate memory references.
+
+
+
+
+
+
+
A.7.2 Inspection and Updates
+
+
+
+These functions examine or update the mappings from pages to frames
+encapsulated by a page table. They work on both active and inactive
+page tables (that is, those for running and suspended processes),
+flushing the TLB as necessary.
+
Adds to pd a mapping from user page upage to the frame identified
+by kernel virtual address kpage. If writable is true, the
+page is mapped read/write; otherwise, it is mapped read-only.
+
+
+User page upage must not already be mapped in pd.
+
+
+
+Kernel page kpage should be a kernel virtual address obtained from
+the user pool with palloc_get_page(PAL_USER) (see Why PAL_USER?).
+
+
+
+Returns true if successful, false on failure. Failure will occur if
+additional memory required for the page table cannot be obtained.
+
Marks page "not present" in pd. Later accesses to
+the page will fault.
+
+
+Other bits in the page table for page are preserved, permitting
+the accessed and dirty bits (see the next section) to be checked.
+
+
+
+This function has no effect if page is not mapped.
+
+
+
+
+
+
+
+
A.7.3 Accessed and Dirty Bits
+
+
+
+80x86 hardware provides some assistance for implementing page
+replacement algorithms, through a pair of bits in the page table entry
+(PTE) for each page. On any read or write to a page, the CPU sets the
+accessed bit to 1 in the page's PTE, and on any write, the CPU
+sets the dirty bit to 1. The CPU never resets these bits to 0,
+but the OS may do so.
+
+
+
+Proper interpretation of these bits requires understanding of
+aliases, that is, two (or more) pages that refer to the same
+frame. When an aliased frame is accessed, the accessed and dirty bits
+are updated in only one page table entry (the one for the page used for
+access). The accessed and dirty bits for the other aliases are not
+updated.
+
+
+
+See Accessed and Dirty Bits, on applying these bits in implementing
+page replacement algorithms.
+
If page directory pd has a page table entry for page, then
+its dirty (or accessed) bit is set to value.
+
+
+
+
+
+
+
A.7.4 Page Table Details
+
+
+
+The functions provided with Pintos are sufficient to implement the
+projects. However, you may still find it worthwhile to understand the
+hardware page table format, so we'll go into a little detail in this
+section.
+
+
+
+
+
+
+
A.7.4.1 Structure
+
+
+
+The top-level paging data structure is a page called the "page
+directory" (PD) arranged as an array of 1,024 32-bit page directory
+entries (PDEs), each of which represents 4 MB of virtual memory. Each
+PDE may point to the physical address of another page called a
+"page table" (PT) arranged, similarly, as an array of 1,024
+32-bit page table entries (PTEs), each of which translates a single 4
+kB virtual page to a physical page.
+
+
+
+Translation of a virtual address into a physical address follows
+the three-step process illustrated in the diagram
+below:(4)
+
+
+
+
+
+The most-significant 10 bits of the virtual address (bits 22...31)
+index the page directory. If the PDE is marked "present," the
+physical address of a page table is read from the PDE thus obtained.
+If the PDE is marked "not present" then a page fault occurs.
+
+
+
+
+The next 10 bits of the virtual address (bits 12...21) index
+the page table. If the PTE is marked "present," the physical
+address of a data page is read from the PTE thus obtained. If the PTE
+is marked "not present" then a page fault occurs.
+
+
+
+
+The least-significant 12 bits of the virtual address (bits 0...11)
+are added to the data page's physical base address, yielding the final
+physical address.
+
+
+Pintos provides some macros and functions that are useful for working
+with raw page tables:
+
+
+
+
+
+
+
Macro:PTSHIFT
+
+
Macro:PTBITS
+
The starting bit index (12) and number of bits (10), respectively, in a
+page table index.
+
+
+
+
+
+
+
Macro:PTMASK
+
A bit mask with the bits in the page table index set to 1 and the rest
+set to 0 (0x3ff000).
+
+
+
+
+
+
+
Macro:PTSPAN
+
The number of bytes of virtual address space that a single page table
+page covers (4,194,304 bytes, or 4 MB).
+
+
+
+
+
+
+
Macro:PDSHIFT
+
+
Macro:PDBITS
+
The starting bit index (22) and number of bits (10), respectively, in a
+page directory index.
+
+
+
+
+
+
+
Macro:PDMASK
+
A bit mask with the bits in the page directory index set to 1 and other
+bits set to 0 (0xffc00000).
+
+
+
+
+
+
+
Function: uintptr_t pd_no (const void *va)
+
+
Function: uintptr_t pt_no (const void *va)
+
Returns the page directory index or page table index, respectively, for
+virtual address va. These functions are defined in
+threads/pte.h.
+
+
+
+
+
+
+
Function: unsigned pg_ofs (const void *va)
+
Returns the page offset for virtual address va. This function is
+defined in threads/vaddr.h.
+
+
+
+
+
+
+
A.7.4.2 Page Table Entry Format
+
+
+
+You do not need to understand the PTE format to do the Pintos
+projects, unless you wish to incorporate the page table into your
+supplemental page table (see Managing the Supplemental Page Table).
+
+
+
+The actual format of a page table entry is summarized below. For
+complete information, refer to section 3.7, "Page Translation Using
+32-Bit Physical Addressing," in [ IA32-v3a].
+
+
+Some more information on each bit is given below. The names are
+threads/pte.h macros that represent the bits' values:
+
+
+
+
+
+
+
Macro:PTE_P
+
Bit 0, the "present" bit. When this bit is 1, the
+other bits are interpreted as described below. When this bit is 0, any
+attempt to access the page will page fault. The remaining bits are then
+not used by the CPU and may be used by the OS for any purpose.
+
+
+
+
+
+
+
Macro:PTE_W
+
Bit 1, the "read/write" bit. When it is 1, the page
+is writable. When it is 0, write attempts will page fault.
+
+
+
+
+
+
+
Macro:PTE_U
+
Bit 2, the "user/supervisor" bit. When it is 1, user
+processes may access the page. When it is 0, only the kernel may access
+the page (user accesses will page fault).
+
+
+Pintos clears this bit in PTEs for kernel virtual memory, to prevent
+user processes from accessing them.
+
Returns a page table entry that points to page, which should be a
+kernel virtual address. The PTE's present bit will be set. It will be
+marked for kernel-only access. If writable is true, the PTE will
+also be marked read/write; otherwise, it will be read-only.
+
Returns a page table entry that points to page, which should be
+the kernel virtual address of a frame in the user pool (see Why PAL_USER?). The PTE's present bit will be set and it will be marked to
+allow user-mode access. If writable is true, the PTE will also be
+marked read/write; otherwise, it will be read-only.
+
+
+
+
+
+
+
Function: void *pte_get_page (uint32_t pte)
+
Returns the kernel virtual address for the frame that pte points
+to. The pte may be present or not-present; if it is not-present
+then the pointer returned is only meaningful if the address bits in the PTE
+actually represent a physical address.
+
+
+
+
+
+
+
A.7.4.3 Page Directory Entry Format
+
+
+
+Page directory entries have the same format as PTEs, except that the
+physical address points to a page table page instead of a frame. Header
+threads/pte.h defines two functions for working with page
+directory entries:
+
+
+
+
+
+
+
Function: uint32_t pde_create (uint32_t *pt)
+
Returns a page directory that points to page, which should be the
+kernel virtual address of a page table page. The PDE's present bit will
+be set, it will be marked to allow user-mode access, and it will be
+marked read/write.
+
+
+
+
+
+
+
Function: uint32_t *pde_get_pt (uint32_t pde)
+
Returns the kernel virtual address for the page table page that
+pde, which must be marked present, points to.
+
+
+
+
+
+
+
A.8 Hash Table
+
+
+
+Pintos provides a hash table data structure in lib/kernel/hash.c.
+To use it you will need to include its header file,
+lib/kernel/hash.h, with #include <hash.h>.
+No code provided with Pintos uses the hash table, which means that you
+are free to use it as is, modify its implementation for your own
+purposes, or ignore it, as you wish.
+
+
+
+Most implementations of the virtual memory project use a hash table to
+translate pages to frames. You may find other uses for hash tables as
+well.
+
+
+
+
+
+
+
A.8.1 Data Types
+
+
+
+A hash table is represented by struct hash.
+
+
+
+
+
+
+
Type:struct hash
+
Represents an entire hash table. The actual members of struct hash
+are "opaque." That is, code that uses a hash table should not access
+struct hash members directly, nor should it need to. Instead, use
+hash table functions and macros.
+
+
+
+The hash table operates on elements of type struct hash_elem.
+
+
+
+
+
+
+
Type:struct hash_elem
+
Embed a struct hash_elem member in the structure you want to include
+in a hash table. Like struct hash, struct hash_elem is opaque.
+All functions for operating on hash table elements actually take and
+return pointers to struct hash_elem, not pointers to your hash table's
+real element type.
+
+
+
+You will often need to obtain a struct hash_elem given a real element
+of the hash table, and vice versa. Given a real element of the hash
+table, you may use the & operator to obtain a pointer to its
+struct hash_elem. Use the hash_entry() macro to go the other
+direction.
+
Returns a pointer to the structure that elem, a pointer to a
+struct hash_elem, is embedded within. You must provide type,
+the name of the structure that elem is inside, and member,
+the name of the member in type that elem points to.
+
+
+For example, suppose h is a struct hash_elem * variable
+that points to a struct thread member (of type struct hash_elem)
+named h_elem. Then, hash_entry@tie{(h, struct thread, h_elem)}
+yields the address of the struct thread that h points within.
+
+
+Each hash table element must contain a key, that is, data that
+identifies and distinguishes elements, which must be unique
+among elements in the hash table. (Elements may
+also contain non-key data that need not be unique.) While an element is
+in a hash table, its key data must not be changed. Instead, if need be,
+remove the element from the hash table, modify its key, then reinsert
+the element.
+
+
+
+For each hash table, you must write two functions that act on keys: a
+hash function and a comparison function. These functions must match the
+following prototypes:
+
Returns a hash of element's data, as a value anywhere in the range
+of unsigned int. The hash of an element should be a
+pseudo-random function of the element's key. It must not depend on
+non-key data in the element or on any non-constant data other than the
+key. Pintos provides the following functions as a suitable basis for
+hash functions.
+
Returns a hash of the size bytes starting at buf. The
+implementation is the general-purpose
+Fowler-Noll-Vo
+hash for 32-bit words.
+
+
+
+
+
+
+
Function: unsigned hash_string (const char *s)
+
Returns a hash of null-terminated string s.
+
+
+
+
+
+
+
Function: unsigned hash_int (int i)
+
Returns a hash of integer i.
+
+
+
+If your key is a single piece of data of an appropriate type, it is
+sensible for your hash function to directly return the output of one of
+these functions. For multiple pieces of data, you may wish to combine
+the output of more than one call to them using, e.g., the ^
+(exclusive or)
+operator. Finally, you may entirely ignore these functions and write
+your own hash function from scratch, but remember that your goal is to
+build an operating system kernel, not to design a hash function.
+
Initializes hash as a hash table with hash_func as hash
+function, less_func as comparison function, and aux as
+auxiliary data.
+Returns true if successful, false on failure. hash_init() calls
+malloc() and fails if memory cannot be allocated.
+
+
+See section A.8.6 Auxiliary Data, for an explanation of aux, which is
+most often a null pointer.
+
Removes all the elements from hash, which must have been
+previously initialized with hash_init().
+
+
+If action is non-null, then it is called once for each element in
+the hash table, which gives the caller an opportunity to deallocate any
+memory or other resources used by the element. For example, if the hash
+table elements are dynamically allocated using malloc(), then
+action could free() the element. This is safe because
+hash_clear() will not access the memory in a given hash element
+after calling action on it. However, action must not call
+any function that may modify the hash table, such as hash_insert()
+or hash_delete().
+
If action is non-null, calls it for each element in the hash, with
+the same semantics as a call to hash_clear(). Then, frees the
+memory held by hash. Afterward, hash must not be passed to
+any hash table function, absent an intervening call to hash_init().
+
+
+
+
+
+
+
Function: size_t hash_size (struct hash *hash)
+
Returns the number of elements currently stored in hash.
+
+
+
+
+
+
+
Function: bool hash_empty (struct hash *hash)
+
Returns true if hash currently contains no elements,
+false if hash contains at least one element.
+
+
+
+
+
+
+
A.8.3 Search Functions
+
+
+
+Each of these functions searches a hash table for an element that
+compares equal to one provided. Based on the success of the search,
+they perform some action, such as inserting a new element into the hash
+table, or simply return the result of the search.
+
Searches hash for an element equal to element. If none is
+found, inserts element into hash and returns a null pointer.
+If the table already contains an element equal to element, it is
+returned without modifying hash.
+
Inserts element into hash. Any element equal to
+element already in hash is removed. Returns the element
+removed, or a null pointer if hash did not contain an element
+equal to element.
+
+
+The caller is responsible for deallocating any resources associated with
+the returned element, as appropriate. For example, if the hash table
+elements are dynamically allocated using malloc(), then the caller
+must free() the element after it is no longer needed.
+
+
+
+
+The element passed to the following functions is only used for hashing
+and comparison purposes. It is never actually inserted into the hash
+table. Thus, only key data in the element needs to be initialized, and
+other data in the element will not be used. It often makes sense to
+declare an instance of the element type as a local variable, initialize
+the key data, and then pass the address of its struct hash_elem to
+hash_find() or hash_delete(). See section A.8.5 Hash Table Example, for
+an example. (Large structures should not be
+allocated as local variables. See section A.2.1 struct thread, for more
+information.)
+
Searches hash for an element equal to element. If one is
+found, it is removed from hash and returned. Otherwise, a null
+pointer is returned and hash is unchanged.
+
+
+The caller is responsible for deallocating any resources associated with
+the returned element, as appropriate. For example, if the hash table
+elements are dynamically allocated using malloc(), then the caller
+must free() the element after it is no longer needed.
+
+
+
+
+
+
+
+
A.8.4 Iteration Functions
+
+
+
+These functions allow iterating through the elements in a hash table.
+Two interfaces are supplied. The first requires writing and supplying a
+hash_action_func to act on each element (see section A.8.1 Data Types).
+
Calls action once for each element in hash, in arbitrary
+order. action must not call any function that may modify the hash
+table, such as hash_insert() or hash_delete(). action
+must not modify key data in elements, although it may modify any other
+data.
+
+
+
+The second interface is based on an "iterator" data type.
+Idiomatically, iterators are used as follows:
+
Represents a position within a hash table. Calling any function that
+may modify a hash table, such as hash_insert() or
+hash_delete(), invalidates all iterators within that hash table.
+
+
+Like struct hash and struct hash_elem, struct hash_elem is opaque.
+
Advances iterator to the next element in hash, and returns
+that element. Returns a null pointer if no elements remain. After
+hash_next() returns null for iterator, calling it again
+yields undefined behavior.
+
Returns the value most recently returned by hash_next() for
+iterator. Yields undefined behavior after hash_first() has
+been called on iterator but before hash_next() has been
+called for the first time.
+
+
+
+
+
+
+
A.8.5 Hash Table Example
+
+
+
+Suppose you have a structure, called struct page, that you
+want to put into a hash table. First, define struct page to include a
+struct hash_elem member:
+
+
+We write a hash function and a comparison function using addr as
+the key. A pointer can be hashed based on its bytes, and the <
+operator works fine for comparing pointers:
+
+
+(The use of UNUSED in these functions' prototypes suppresses a
+warning that aux is unused. See section D.3 Function and Parameter Attributes, for information about UNUSED. See section A.8.6 Auxiliary Data, for an explanation of aux.)
+
+
+Now we can manipulate the hash table we've created. If p
+is a pointer to a struct page, we can insert it into the hash table
+with:
+
+
+
+
hash_insert (&pages, &p->hash_elem);
+
+
+If there's a chance that pages might already contain a
+page with the same addr, then we should check hash_insert()'s
+return value.
+
+
+
+To search for an element in the hash table, use hash_find(). This
+takes a little setup, because hash_find() takes an element to
+compare against. Here's a function that will find and return a page
+based on a virtual address, assuming that pages is defined at file
+scope:
+
+
+
+
/* Returns the page containing the given virtual address,
+ or a null pointer if no such page exists. */
+struct page *
+page_lookup (const void *address)
+{
+ struct page p;
+ struct hash_elem *e;
+
+ p.addr = address;
+ e = hash_find (&pages, &p.hash_elem);
+ return e != NULL ? hash_entry (e, struct page, hash_elem) : NULL;
+}
+
+
+struct page is allocated as a local variable here on the assumption
+that it is fairly small. Large structures should not be allocated as
+local variables. See section A.2.1 struct thread, for more information.
+
+
+
+A similar function could delete a page by address using
+hash_delete().
+
+
+
+
+
+
+
A.8.6 Auxiliary Data
+
+
+
+In simple cases like the example above, there's no need for the
+aux parameters. In these cases, just pass a null pointer to
+hash_init() for aux and ignore the values passed to the hash
+function and comparison functions. (You'll get a compiler warning if
+you don't use the aux parameter, but you can turn that off with
+the UNUSED macro, as shown in the example, or you can just ignore
+it.)
+
+
+
+aux is useful when you have some property of the data in the
+hash table is both constant and needed for hashing or comparison,
+but not stored in the data items themselves. For example, if
+the items in a hash table are fixed-length strings, but the items
+themselves don't indicate what that fixed length is, you could pass
+the length as an aux parameter.
+
+
+
+
+
+
+
A.8.7 Synchronization
+
+
+
+The hash table does not do any internal synchronization. It is the
+caller's responsibility to synchronize calls to hash table functions.
+In general, any number of functions that examine but do not modify the
+hash table, such as hash_find() or hash_next(), may execute
+simultaneously. However, these function cannot safely execute at the
+same time as any function that may modify a given hash table, such as
+hash_insert() or hash_delete(), nor may more than one function
+that can modify a given hash table execute safely at once.
+
+
+
+It is also the caller's responsibility to synchronize access to data in
+hash table elements. How to synchronize access to this data depends on
+how it is designed and organized, as with any other data structure.
+
+
+TODO: TUW coding standards
+We expect you to be
+familiar with some set of coding standards such as
+CS 107 Coding Standards. Even if you've taken 107, we recommend
+reviewing that document. We expect code at the "Peer-Review Quality"
+level described there.
+
+
+
+Our standards for coding are most important for grading. We want to
+stress that aside from the fact that we are explicitly basing part of
+your grade on these things, good coding practices will improve the
+quality of your code. This makes it easier for your partners to
+interact with it, and ultimately, will improve your chances of having a
+good working program. That said once, the rest of this document will
+discuss only the ways in which our coding standards will affect our
+grading.
+
+
+
+
+
+
+
B.1 Style
+
+
+
+Style, for the purposes of our grading, refers to how readable your
+code is. At minimum, this means that your code is well formatted, your
+variable names are descriptive and your functions are decomposed and
+well commented. Any other factors which make it hard (or easy) for us
+to read or use your code will be reflected in your style grade.
+
+
+
+The existing Pintos code is written in the GNU style and largely
+follows the GNU
+Coding Standards. We encourage you to follow the applicable parts of
+them too, especially chapter 5, "Making the Best Use of C." Using a
+different style won't cause actual problems, but it's ugly to see
+gratuitous differences in style from one function to another. If your
+code is too ugly, it will cost you points.
+
+
+
+Please limit C source file lines to at most 79 characters long.
+
+
+
+Pintos comments sometimes refer to external standards or
+specifications by writing a name inside square brackets, like this:
+[IA32-v3a]. These names refer to the reference names used in
+this documentation (see section Bibliography).
+
+
+
+If you remove existing Pintos code, please delete it from your source
+file entirely. Don't just put it into a comment or a conditional
+compilation directive, because that makes the resulting code hard to
+read.
+
+
+
+We're only going to do a compile in the directory for the project being
+submitted. You don't need to make sure that the previous projects also
+compile.
+
+
+
+Project code should be written so that all of the subproblems for the
+project function together, that is, without the need to rebuild with
+different macros defined, etc. If you do extra credit work that
+changes normal Pintos behavior so as to interfere with grading, then
+you must implement it so that it only acts that way when given a
+special command-line option of the form -name, where
+name is a name of your choice. You can add such an option by
+modifying parse_options() in threads/init.c.
+
+
+The Pintos source code uses a few features of the "C99" standard
+library that were not in the original 1989 standard for C. Many
+programmers are unaware of these feature, so we will describe them. The
+new features used in Pintos are
+mostly in new headers:
+
+
+
+
+
+
<stdbool.h>
+
Defines macros bool, a 1-bit type that takes on only the values
+0 and 1, true, which expands to 1, and false, which
+expands to 0.
+
+
+
+
<stdint.h>
+
On systems that support them, this header defines types
+intn_t and uintn_t for n = 8, 16, 32,
+64, and possibly other values. These are 2's complement signed and unsigned
+types, respectively, with the given number of bits.
+
+
+On systems where it is possible, this header also defines types
+intptr_t and uintptr_t, which are integer types big
+enough to hold a pointer.
+
+
+
+On all systems, this header defines types intmax_t and
+uintmax_t, which are the system's signed and unsigned integer
+types with the widest ranges.
+
+
+
+For every signed integer type type_t defined here, as well
+as for ptrdiff_t defined in <stddef.h>, this header also
+defines macros TYPE_MAX and TYPE_MIN that
+give the type's range. Similarly, for every unsigned integer type
+type_t defined here, as well as for size_t defined
+in <stddef.h>, this header defines a TYPE_MAX
+macro giving its maximum value.
+
+
+
+
+
<inttypes.h>
+
<stdint.h> provides no straightforward way to format
+the types it defines with printf() and related functions. This
+header provides macros to help with that. For every
+intn_t defined by <stdint.h>, it provides macros
+PRIdn and PRIin for formatting values of
+that type with "%d" and "%i". Similarly, for every
+uintn_t, it provides PRIon,
+PRIun, PRIux, and PRIuX.
+
+
+You use these something like this, taking advantage of the fact that
+the C compiler concatenates adjacent string literals:
+
The % is not supplied by the PRI macros. As shown
+above, you supply it yourself and follow it by any flags, field
+width, etc.
+
+
+
+
<stdio.h>
+
The printf() function has some new type modifiers for printing
+standard types:
+
+
+
+
+
j
+
For intmax_t (e.g. %jd) or uintmax_t (e.g.
+%ju).
+
+
+
+
z
+
For size_t (e.g. %zu).
+
+
+
+
t
+
For ptrdiff_t (e.g. %td).
+
+
+
+Pintos printf() also implements a nonstandard ' flag that
+groups large numbers with commas to make them easier to read.
+
+
+
+
+
+
+
B.3 Unsafe String Functions
+
+
+
+A few of the string functions declared in the standard
+<string.h> and <stdio.h> headers are notoriously unsafe.
+The worst offenders are intentionally not included in the Pintos C
+library:
+
+
+
+
+
+
strcpy
+
When used carelessly this function can overflow the buffer reserved
+for its output string. Use strlcpy() instead. Refer to
+comments in its source code in lib/string.c for documentation.
+
+
+
+
strncpy
+
This function can leave its destination buffer without a null string
+terminator. It also has performance problems. Again, use
+strlcpy().
+
+
+
+
strcat
+
Same issue as strcpy(). Use strlcat() instead.
+Again, refer to comments in its source code in lib/string.c for
+documentation.
+
+
+
+
strncat
+
The meaning of its buffer size argument is surprising.
+Again, use strlcat().
+
+
+
+
strtok
+
Uses global data, so it is unsafe in threaded programs such as
+kernels. Use strtok_r() instead, and see its source code in
+lib/string.c for documentation and an example.
+
+
+
+
sprintf
+
Same issue as strcpy(). Use snprintf() instead. Refer
+to comments in lib/stdio.h for documentation.
+
+
+
+
vsprintf
+
Same issue as strcpy(). Use vsnprintf() instead.
+
+
+
+If you try to use any of these functions, the error message will give
+you a hint by referring to an identifier like
+dont_use_sprintf_use_snprintf.
+
+
+
+This chapter presents a sample assignment and a filled-in design
+document for one possible implementation. Its purpose is to give you an
+idea of what we expect to see in your own design documents.
+
+
+
+
+
+
+
C.1 Sample Assignment
+
+
+
+Implement thread_join().
+
+
+
+
+
+
+
Function: void thread_join (tid_t tid)
+
Blocks the current thread until thread tid exits. If A is
+the running thread and B is the argument, then we say that
+"A joins B."
+
+
+Incidentally, the argument is a thread id, instead of a thread pointer,
+because a thread pointer is not unique over time. That is, when a
+thread dies, its memory may be, whether immediately or much later,
+reused for another thread. If thread A over time had two children
+B and C that were stored at the same address, then
+thread_join(B) and thread_join(C) would be
+ambiguous.
+
+
+
+A thread may only join its immediate children. Calling
+thread_join() on a thread that is not the caller's child should
+cause the caller to return immediately. Children are not "inherited,"
+that is, if A has child B and B has child C,
+then A always returns immediately should it try to join C,
+even if B is dead.
+
+
+
+A thread need not ever be joined. Your solution should properly free
+all of a thread's resources, including its struct thread,
+whether it is ever joined or not, and regardless of whether the child
+exits before or after its parent. That is, a thread should be freed
+exactly once in all cases.
+
+
+
+Joining a given thread is idempotent. That is, joining a thread
+multiple times is equivalent to joining it once, because it has already
+exited at the time of the later joins. Thus, joins on a given thread
+after the first should return immediately.
+
+
+
+You must handle all the ways a join can occur: nested joins (A
+joins B, then B joins C), multiple joins (A
+joins B, then A joins C), and so on.
+
+
+
+
+
+
+
+
C.2 Sample Design Document
+
+
+
+
+ +-----------------+
+ | CS 140 |
+ | SAMPLE PROJECT |
+ | DESIGN DOCUMENT |
+ +-----------------+
+
+---- GROUP ----
+
+Ben Pfaff <blp@stanford.edu>
+
+---- PRELIMINARIES ----
+
+>> If you have any preliminary comments on your submission, notes for
+>> the TAs, or extra credit, please give them here.
+
+(This is a sample design document.)
+
+>> Please cite any offline or online sources you consulted while
+>> preparing your submission, other than the Pintos documentation,
+>> course text, and lecture notes.
+
+None.
+
+ JOIN
+ ====
+
+---- DATA STRUCTURES ----
+
+>> 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.
+
+A "latch" is a new synchronization primitive. Acquires block
+until the first release. Afterward, all ongoing and future
+acquires pass immediately.
+
+ /* Latch. */
+ struct latch
+ {
+ bool released; /* Released yet? */
+ struct lock monitor_lock; /* Monitor lock. */
+ struct condition rel_cond; /* Signaled when released. */
+ };
+
+Added to struct thread:
+
+ /* Members for implementing thread_join(). */
+ struct latch ready_to_die; /* Release when thread about to die. */
+ struct semaphore can_die; /* Up when thread allowed to die. */
+ struct list children; /* List of child threads. */
+ list_elem children_elem; /* Element of `children' list. */
+
+---- ALGORITHMS ----
+
+>> Briefly describe your implementation of thread_join() and how it
+>> interacts with thread termination.
+
+thread_join() finds the joined child on the thread's list of
+children and waits for the child to exit by acquiring the child's
+ready_to_die latch. When thread_exit() is called, the thread
+releases its ready_to_die latch, allowing the parent to continue.
+
+---- SYNCHRONIZATION ----
+
+>> Consider parent thread P with child thread C. How do you ensure
+>> proper synchronization and avoid race conditions when P calls wait(C)
+>> before C exits? After C exits? How do you ensure that all resources
+>> are freed in each case? How about when P terminates without waiting,
+>> before C exits? After C exits? Are there any special cases?
+
+C waits in thread_exit() for P to die before it finishes its own
+exit, using the can_die semaphore "down"ed by C and "up"ed by P as
+it exits. Regardless of whether whether C has terminated, there
+is no race on wait(C), because C waits for P's permission before
+it frees itself.
+
+Regardless of whether P waits for C, P still "up"s C's can_die
+semaphore when P dies, so C will always be freed. (However,
+freeing C's resources is delayed until P's death.)
+
+The initial thread is a special case because it has no parent to
+wait for it or to "up" its can_die semaphore. Therefore, its
+can_die semaphore is initialized to 1.
+
+---- RATIONALE ----
+
+>> Critique your design, pointing out advantages and disadvantages in
+>> your design choices.
+
+This design has the advantage of simplicity. Encapsulating most
+of the synchronization logic into a new "latch" structure
+abstracts what little complexity there is into a separate layer,
+making the design easier to reason about. Also, all the new data
+members are in `struct thread', with no need for any extra dynamic
+allocation, etc., that would require extra management code.
+
+On the other hand, this design is wasteful in that a child thread
+cannot free itself before its parent has terminated. A parent
+thread that creates a large number of short-lived child threads
+could unnecessarily exhaust kernel memory. This is probably
+acceptable for implementing kernel threads, but it may be a bad
+idea for use with user processes because of the larger number of
+resources that user processes tend to own.
+
+
+Many tools lie at your disposal for debugging Pintos. This appendix
+introduces you to a few of them.
+
+
+
+
+
+
+
D.1 printf()
+
+
+
+Don't underestimate the value of printf(). The way
+printf() is implemented in Pintos, you can call it from
+practically anywhere in the kernel, whether it's in a kernel thread or
+an interrupt handler, almost regardless of what locks are held.
+
+
+
+printf() is useful for more than just examining data.
+It can also help figure out when and where something goes wrong, even
+when the kernel crashes or panics without a useful error message. The
+strategy is to sprinkle calls to printf() with different strings
+(e.g. "<1>", "<2>", ...) throughout the pieces of
+code you suspect are failing. If you don't even see <1> printed,
+then something bad happened before that point, if you see <1>
+but not <2>, then something bad happened between those two
+points, and so on. Based on what you learn, you can then insert more
+printf() calls in the new, smaller region of code you suspect.
+Eventually you can narrow the problem down to a single statement.
+See section D.6 Triple Faults, for a related technique.
+
+
+
+
+
+
+
D.2 ASSERT
+
+
+
+Assertions are useful because they can catch problems early, before
+they'd otherwise be noticed. Ideally, each function should begin with a
+set of assertions that check its arguments for validity. (Initializers
+for functions' local variables are evaluated before assertions are
+checked, so be careful not to assume that an argument is valid in an
+initializer.) You can also sprinkle assertions throughout the body of
+functions in places where you suspect things are likely to go wrong.
+They are especially useful for checking loop invariants.
+
+
+
+Pintos provides the ASSERT macro, defined in <debug.h>,
+for checking assertions.
+
+
+
+
+
+
+
Macro:ASSERT(expression)
+
Tests the value of expression. If it evaluates to zero (false),
+the kernel panics. The panic message includes the expression that
+failed, its file and line number, and a backtrace, which should help you
+to find the problem. See section D.4 Backtraces, for more information.
+
+
+
+
+
+
+
D.3 Function and Parameter Attributes
+
+
+
+These macros defined in <debug.h> tell the compiler special
+attributes of a function or function parameter. Their expansions are
+GCC-specific.
+
+
+
+
+
+
+
Macro:UNUSED
+
Appended to a function parameter to tell the compiler that the
+parameter might not be used within the function. It suppresses the
+warning that would otherwise appear.
+
+
+
+
+
+
+
Macro:NO_RETURN
+
Appended to a function prototype to tell the compiler that the
+function never returns. It allows the compiler to fine-tune its
+warnings and its code generation.
+
+
+
+
+
+
+
Macro:NO_INLINE
+
Appended to a function prototype to tell the compiler to never emit
+the function in-line. Occasionally useful to improve the quality of
+backtraces (see below).
+
+
+
+
+
+
+
Macro:PRINTF_FORMAT(format, first)
+
Appended to a function prototype to tell the compiler that the function
+takes a printf()-like format string as the argument numbered
+format (starting from 1) and that the corresponding value
+arguments start at the argument numbered first. This lets the
+compiler tell you if you pass the wrong argument types.
+
+
+
+
+
+
+
D.4 Backtraces
+
+
+
+When the kernel panics, it prints a "backtrace," that is, a summary
+of how your program got where it is, as a list of addresses inside the
+functions that were running at the time of the panic. You can also
+insert a call to debug_backtrace(), prototyped in
+<debug.h>, to print a backtrace at any point in your code.
+debug_backtrace_all(), also declared in <debug.h>,
+prints backtraces of all threads.
+
+
+
+The addresses in a backtrace are listed as raw hexadecimal numbers,
+which are difficult to interpret. We provide a tool called
+backtrace to translate these into function names and source
+file line numbers.
+Give it the name of your kernel.o as the first argument and the
+hexadecimal numbers composing the backtrace (including the 0x
+prefixes) as the remaining arguments. It outputs the function name
+and source file line numbers that correspond to each address.
+
+
+
+If the translated form of a backtrace is garbled, or doesn't make
+sense (e.g. function A is listed above function B, but B doesn't
+call A), then it's a good sign that you're corrupting a kernel
+thread's stack, because the backtrace is extracted from the stack.
+Alternatively, it could be that the kernel.o you passed to
+backtrace is not the same kernel that produced
+the backtrace.
+
+
+
+Sometimes backtraces can be confusing without any corruption.
+Compiler optimizations can cause surprising behavior. When a function
+has called another function as its final action (a tail call), the
+calling function may not appear in a backtrace at all. Similarly, when
+function A calls another function B that never returns, the compiler may
+optimize such that an unrelated function C appears in the backtrace
+instead of A. Function C is simply the function that happens to be in
+memory just after A. In the threads project, this is commonly seen in
+backtraces for test failures.
+
+
+
+
+
+
+
D.4.1 Example
+
+
+
+Here's an example. Suppose that Pintos printed out this following call
+stack, which is taken from an actual Pintos submission for the file
+system project:
+
+
+You would then invoke the backtrace utility like shown below,
+cutting and pasting the backtrace information into the command line.
+This assumes that kernel.o is in the current directory. You
+would of course enter all of the following on a single shell command
+line, even though that would overflow our margins here:
+
+
+(You will probably not see exactly the same addresses if you run the
+command above on your own kernel binary, because the source code you
+compiled and the compiler you used are probably different.)
+
+
+
+The first line in the backtrace refers to debug_panic(), the
+function that implements kernel panics. Because backtraces commonly
+result from kernel panics, debug_panic() will often be the first
+function shown in a backtrace.
+
+
+
+The second line shows file_seek() as the function that panicked,
+in this case as the result of an assertion failure. In the source code
+tree used for this example, line 405 of filesys/file.c is the
+assertion
+
+
+
+
ASSERT (file_ofs >= 0);
+
+
+(This line was also cited in the assertion failure message.)
+Thus, file_seek() panicked because it passed a negative file offset
+argument.
+
+
+
+The third line indicates that seek() called file_seek(),
+presumably without validating the offset argument. In this submission,
+seek() implements the seek system call.
+
+
+
+The fourth line shows that syscall_handler(), the system call
+handler, invoked seek().
+
+
+
+The fifth and sixth lines are the interrupt handler entry path.
+
+
+
+The remaining lines are for addresses below PHYS_BASE. This
+means that they refer to addresses in the user program, not in the
+kernel. If you know what user program was running when the kernel
+panicked, you can re-run backtrace on the user program, like
+so: (typing the command on a single line, of course):
+
+
+Here's an extra tip for anyone who read this far: backtrace
+is smart enough to strip the Call stack: header and .
+trailer from the command line if you include them. This can save you
+a little bit of trouble in cutting and pasting. Thus, the following
+command prints the same output as the first one we used:
+
+
+You can run Pintos under the supervision of the GDB debugger.
+First, start Pintos with the --gdb option, e.g.
+pintos --gdb -- run mytest. Second, open a second terminal on
+the same machine and
+use pintos-gdb to invoke GDB on
+kernel.o:(5)
+
pintos-gdb kernel.o
+
and issue the following GDB command:
+
target remote localhost:1234
+
+
+Now GDB is connected to the simulator over a local
+network connection. You can now issue any normal GDB
+commands. If you issue the c command, the simulated BIOS will take
+control, load Pintos, and then Pintos will run in the usual way. You
+can pause the process at any point with Ctrl+C.
+
+
+
+
+
+
+
D.5.1 Using GDB
+
+
+
+You can read the GDB manual by typing info gdb at a
+terminal command prompt. Here's a few commonly useful GDB commands:
+
+
+
+
+
+
+
GDB Command:c
+
Continues execution until Ctrl+C or the next breakpoint.
+
+
+
+
+
+
+
GDB Command:breakfunction
+
+
GDB Command:breakfile:line
+
+
GDB Command:break*address
+
Sets a breakpoint at function, at line within file, or
+address.
+(Use a 0x prefix to specify an address in hex.)
+
+
+Use break main to make GDB stop when Pintos starts running.
+
+
+
+
+
+
+
+
GDB Command:pexpression
+
Evaluates the given expression and prints its value.
+If the expression contains a function call, that function will actually
+be executed.
+
+
+
+
+
+
+
GDB Command:l*address
+
Lists a few lines of code around address.
+(Use a 0x prefix to specify an address in hex.)
+
+
+
+
+
+
+
GDB Command:bt
+
Prints a stack backtrace similar to that output by the
+backtrace program described above.
+
+
+
+
+
+
+
GDB Command:p/aaddress
+
Prints the name of the function or variable that occupies address.
+(Use a 0x prefix to specify an address in hex.)
+
+
+
+
+
+
+
GDB Command:diassemblefunction
+
Disassembles function.
+
+
+
+We also provide a set of macros specialized for debugging Pintos,
+written by Godmar Back gback@cs.vt.edu. You can type
+help user-defined for basic help with the macros. Here is an
+overview of their functionality, based on Godmar's documentation:
+
+
+
+
+
+
+
GDB Macro:debugpintos
+
Attach debugger to a waiting pintos process on the same machine.
+Shorthand for target remote localhost:1234.
+
+
+
+
+
+
+
GDB Macro:dumplistlist type element
+
Prints the elements of list, which should be a struct list
+that contains elements of the given type (without the word
+struct) in which element is the struct list_elem member
+that links the elements.
+
+
+Example: dumplist all_list thread allelem prints all elements of
+struct thread that are linked in struct list all_list using the
+struct list_elem allelem which is part of struct thread.
+
+
+
+
+
+
+
+
GDB Macro:btthreadthread
+
Shows the backtrace of thread, which is a pointer to the
+struct thread of the thread whose backtrace it should show. For the
+current thread, this is identical to the bt (backtrace) command.
+It also works for any thread suspended in schedule(),
+provided you know where its kernel stack page is located.
+
+
+
+
+
+
+
GDB Macro:btthreadlistlist element
+
Shows the backtraces of all threads in list, the struct list in
+which the threads are kept. Specify element as the
+struct list_elem field used inside struct thread to link the threads
+together.
+
+
+Example: btthreadlist all_list allelem shows the backtraces of
+all threads contained in struct list all_list, linked together by
+allelem. This command is useful to determine where your threads
+are stuck when a deadlock occurs. Please see the example scenario below.
+
+
+
+
+
+
+
+
GDB Macro:btthreadall
+
Short-hand for btthreadlist all_list allelem.
+
+
+
+
+
+
+
GDB Macro:btpagefault
+
Print a backtrace of the current thread after a page fault exception.
+Normally, when a page fault exception occurs, GDB will stop
+with a message that might say:(6)
+
+
+
Program received signal 0, Signal 0.
+0xc0102320 in intr0e_stub ()
+
+
+In that case, the bt command might not give a useful
+backtrace. Use btpagefault instead.
+
+
+
+You may also use btpagefault for page faults that occur in a user
+process. In this case, you may wish to also load the user program's
+symbol table using the loadusersymbols macro, as described above.
+
+
+
+
+
+
+
+
GDB Macro:hook-stop
+
GDB invokes this macro every time the simulation stops, which Bochs will
+do for every processor exception, among other reasons. If the
+simulation stops due to a page fault, hook-stop will print a
+message that says and explains further whether the page fault occurred
+in the kernel or in user code.
+
+
+If the exception occurred from user code, hook-stop will say:
+
pintos-debug: a page fault exception occurred in user mode
+pintos-debug: hit 'c' to continue, or 's' to step to intr_handler
+
+
+In Project 2, a page fault in a user process leads to the termination of
+the process. You should expect those page faults to occur in the
+robustness tests where we test that your kernel properly terminates
+processes that try to access invalid addresses. To debug those, set a
+break point in page_fault() in exception.c, which you will
+need to modify accordingly.
+
+
+
+In Project 3, a page fault in a user process no longer automatically
+leads to the termination of a process. Instead, it may require reading in
+data for the page the process was trying to access, either
+because it was swapped out or because this is the first time it's
+accessed. In either case, you will reach page_fault() and need to
+take the appropriate action there.
+
+
+
+If the page fault did not occur in user mode while executing a user
+process, then it occurred in kernel mode while executing kernel code.
+In this case, hook-stop will print this message:
+
pintos-debug: a page fault occurred in kernel mode
+
followed by the output of the btpagefault command.
+
+
+Before Project 3, a page fault exception in kernel code is always a bug
+in your kernel, because your kernel should never crash. Starting with
+Project 3, the situation will change if you use the get_user() and
+put_user() strategy to verify user memory accesses
+(see section 2.2.5 Accessing User Memory).
+
+
+
+
+
+
+
+
+
+
+
D.5.2 Example GDB Session
+
+
+
+This section narrates a sample GDB session, provided by Godmar Back.
+This example illustrates how one might debug a Project 1 solution in
+which occasionally a thread that calls timer_sleep() is not woken
+up. With this bug, tests such as mlfqs_load_1 get stuck.
+
+
+
+This session was captured with a slightly older version of Bochs and the
+GDB macros for Pintos, so it looks slightly different than it would now.
+Program output is shown in normal type, user input in strong
+type.
+
+
+
+First, I start Pintos:
+
+
+
+
$ pintos -v --gdb -- -q -mlfqs run mlfqs-load-1
+Writing command line to /tmp/gDAlqTB5Uf.dsk...
+bochs -q
+========================================================================
+ Bochs x86 Emulator 2.2.5
+ Build from CVS snapshot on December 30, 2005
+========================================================================
+00000000000i[ ] reading configuration from bochsrc.txt
+00000000000i[ ] Enabled gdbstub
+00000000000i[ ] installing nogui module as the Bochs GUI
+00000000000i[ ] using log file bochsout.txt
+Waiting for gdb connection on localhost:1234
+
+
+Then, I open a second window on the same machine and start GDB:
+
+
+
+
$ pintos-gdb kernel.o
+GNU gdb Red Hat Linux (6.3.0.0-1.84rh)
+Copyright 2004 Free Software Foundation, Inc.
+GDB is free software, covered by the GNU General Public License, and you are
+welcome to change it and/or distribute copies of it under certain conditions.
+Type "show copying" to see the conditions.
+There is absolutely no warranty for GDB. Type "show warranty" for details.
+This GDB was configured as "i386-redhat-linux-gnu"...
+Using host libthread_db library "/lib/libthread_db.so.1".
+
+
+Then, I tell GDB to attach to the waiting Pintos emulator:
+
+
+
+
(gdb) debugpintos
+Remote debugging using localhost:1234
+0x0000fff0 in ?? ()
+Reply contains invalid hex digit 78
+
+
+Now I tell Pintos to run by executing c (short for
+continue) twice:
+
+
+
+
(gdb) c
+Continuing.
+Reply contains invalid hex digit 78
+(gdb) c
+Continuing.
+
+
+Now Pintos will continue and output:
+
+
+
+
Pintos booting with 4,096 kB RAM...
+Kernel command line: -q -mlfqs run mlfqs-load-1
+374 pages available in kernel pool.
+373 pages available in user pool.
+Calibrating timer... 102,400 loops/s.
+Boot complete.
+Executing 'mlfqs-load-1':
+(mlfqs-load-1) begin
+(mlfqs-load-1) spinning for up to 45 seconds, please wait...
+(mlfqs-load-1) load average rose to 0.5 after 42 seconds
+(mlfqs-load-1) sleeping for another 10 seconds, please wait...
+
+
+...until it gets stuck because of the bug I had introduced. I hit
+Ctrl+C in the debugger window:
+
+
+
+
Program received signal 0, Signal 0.
+0xc010168c in next_thread_to_run () at ../../threads/thread.c:649
+649 while (i <= PRI_MAX && list_empty (&ready_list[i]))
+(gdb)
+
+
+The thread that was running when I interrupted Pintos was the idle
+thread. If I run backtrace, it shows this backtrace:
+
+
+
+
(gdb) bt
+#0 0xc010168c in next_thread_to_run () at ../../threads/thread.c:649
+#1 0xc0101778 in schedule () at ../../threads/thread.c:714
+#2 0xc0100f8f in thread_block () at ../../threads/thread.c:324
+#3 0xc0101419 in idle (aux=0x0) at ../../threads/thread.c:551
+#4 0xc010145a in kernel_thread (function=0xc01013ff , aux=0x0)
+ at ../../threads/thread.c:575
+#5 0x00000000 in ?? ()
+
+
+Not terribly useful. What I really like to know is what's up with the
+other thread (or threads). Since I keep all threads in a linked list
+called all_list, linked together by a struct list_elem member
+named allelem, I can use the btthreadlist macro from the
+macro library I wrote. btthreadlist iterates through the list of
+threads and prints the backtrace for each thread:
+
+
+
+
(gdb) btthreadlist all_list allelem
+pintos-debug: dumping backtrace of thread 'main' @0xc002f000
+#0 0xc0101820 in schedule () at ../../threads/thread.c:722
+#1 0xc0100f8f in thread_block () at ../../threads/thread.c:324
+#2 0xc0104755 in timer_sleep (ticks=1000) at ../../devices/timer.c:141
+#3 0xc010bf7c in test_mlfqs_load_1 () at ../../tests/threads/mlfqs-load-1.c:49
+#4 0xc010aabb in run_test (name=0xc0007d8c "mlfqs-load-1")
+ at ../../tests/threads/tests.c:50
+#5 0xc0100647 in run_task (argv=0xc0110d28) at ../../threads/init.c:281
+#6 0xc0100721 in run_actions (argv=0xc0110d28) at ../../threads/init.c:331
+#7 0xc01000c7 in main () at ../../threads/init.c:140
+
+pintos-debug: dumping backtrace of thread 'idle' @0xc0116000
+#0 0xc010168c in next_thread_to_run () at ../../threads/thread.c:649
+#1 0xc0101778 in schedule () at ../../threads/thread.c:714
+#2 0xc0100f8f in thread_block () at ../../threads/thread.c:324
+#3 0xc0101419 in idle (aux=0x0) at ../../threads/thread.c:551
+#4 0xc010145a in kernel_thread (function=0xc01013ff , aux=0x0)
+ at ../../threads/thread.c:575
+#5 0x00000000 in ?? ()
+
+
+In this case, there are only two threads, the idle thread and the main
+thread. The kernel stack pages (to which the struct thread points)
+are at 0xc0116000 and 0xc002f000, respectively. The main thread
+is stuck in timer_sleep(), called from test_mlfqs_load_1.
+
+
+
+Knowing where threads are stuck can be tremendously useful, for instance
+when diagnosing deadlocks or unexplained hangs.
+
+
+
+
+
+
+
GDB Macro:loadusersymbols
+
+
+You can also use GDB to debug a user program running under Pintos.
+To do that, use the loadusersymbols macro to load the program's
+symbol table:
+
loadusersymbols program
+
where program is the name of the program's executable (in the host
+file system, not in the Pintos file system). For example, you may issue:
+
(gdb) loadusersymbols tests/userprog/exec-multiple
+add symbol table from file "tests/userprog/exec-multiple" at
+ .text_addr = 0x80480a0
+(gdb)
+
+
+After this, you should be
+able to debug the user program the same way you would the kernel, by
+placing breakpoints, inspecting data, etc. Your actions apply to every
+user program running in Pintos, not just to the one you want to debug,
+so be careful in interpreting the results: GDB does not know
+which process is currently active (because that is an abstraction
+the Pintos kernel creates). Also, a name that appears in
+both the kernel and the user program will actually refer to the kernel
+name. (The latter problem can be avoided by giving the user executable
+name on the GDB command line, instead of kernel.o, and then using
+loadusersymbols to load kernel.o.)
+loadusersymbols is implemented via GDB's add-symbol-file
+command.
+
+
+
+
+
+
+
+
+
+
+
D.5.3 FAQ
+
+
+
+
+
+
GDB can't connect to Bochs.
+
+
+If the target remote command fails, then make sure that both
+GDB and pintos are running on the same machine by
+running hostname in each terminal. If the names printed
+differ, then you need to open a new terminal for GDB on the
+machine running pintos.
+
+
+
+
+
GDB doesn't recognize any of the macros.
+
+
+If you start GDB with pintos-gdb, it should load the Pintos
+macros automatically. If you start GDB some other way, then you must
+issue the command source pintosdir/src/misc/gdb-macros,
+where pintosdir is the root of your Pintos directory, before you
+can use them.
+
+
+
+
+
Can I debug Pintos with DDD?
+
+
+Yes, you can. DDD invokes GDB as a subprocess, so you'll need to tell
+it to invokes pintos-gdb instead:
+
ddd --gdb --debugger pintos-gdb
+
+
+
+
Can I use GDB inside Emacs?
+
+
+Yes, you can. Emacs has special support for running GDB as a
+subprocess. Type M-x gdb and enter your pintos-gdb
+command at the prompt. The Emacs manual has information on how to use
+its debugging features in a section titled "Debuggers."
+
+
+
+
+
GDB is doing something weird.
+
+
+If you notice strange behavior while using GDB, there
+are three possibilities: a bug in your
+modified Pintos, a bug in Bochs's
+interface to GDB or in GDB itself, or
+a bug in the original Pintos code. The first and second
+are quite likely, and you should seriously consider both. We hope
+that the third is less likely, but it is also possible.
+
+
+
+
+
+
+
D.6 Triple Faults
+
+
+
+When a CPU exception handler, such as a page fault handler, cannot be
+invoked because it is missing or defective, the CPU will try to invoke
+the "double fault" handler. If the double fault handler is itself
+missing or defective, that's called a "triple fault." A triple fault
+causes an immediate CPU reset.
+
+
+
+Thus, if you get yourself into a situation where the machine reboots in
+a loop, that's probably a "triple fault." In a triple fault
+situation, you might not be able to use printf() for debugging,
+because the reboots might be happening even before everything needed for
+printf() is initialized.
+
+
+
+There are at least two ways to debug triple faults. First, you can run
+Pintos in Bochs under GDB (see section D.5 GDB). If Bochs has been built
+properly for Pintos, a triple fault under GDB will cause it to print the
+message "Triple fault: stopping for gdb" on the console and break into
+the debugger. (If Bochs is not running under GDB, a triple fault will
+still cause it to reboot.) You can then inspect where Pintos stopped,
+which is where the triple fault occurred.
+
+
+
+Another option is what I call "debugging by infinite loop."
+Pick a place in the Pintos code, insert the infinite loop
+for (;;); there, and recompile and run. There are two likely
+possibilities:
+
+
+
+
+
+The machine hangs without rebooting. If this happens, you know that
+the infinite loop is running. That means that whatever caused the
+reboot must be after the place you inserted the infinite loop.
+Now move the infinite loop later in the code sequence.
+
+
+
+
+The machine reboots in a loop. If this happens, you know that the
+machine didn't make it to the infinite loop. Thus, whatever caused the
+reboot must be before the place you inserted the infinite loop.
+Now move the infinite loop earlier in the code sequence.
+
+
+
+If you move around the infinite loop in a "binary search" fashion, you
+can use this technique to pin down the exact spot that everything goes
+wrong. It should only take a few minutes at most.
+
+
+
+
+
+
+
D.7 Modifying Bochs
+
+
+
+An advanced debugging technique is to modify and recompile the
+simulator. This proves useful when the simulated hardware has more
+information than it makes available to the OS. For example, page
+faults have a long list of potential causes, but the hardware does not
+report to the OS exactly which one is the particular cause.
+Furthermore, a bug in the kernel's handling of page faults can easily
+lead to recursive faults, but a "triple fault" will cause the CPU to
+reset itself, which is hardly conducive to debugging.
+
+
+
+In a case like this, you might appreciate being able to make Bochs
+print out more debug information, such as the exact type of fault that
+occurred. It's not very hard. You start by retrieving the source
+code for Bochs 2.2.6 from http://bochs.sourceforge.net and
+saving the file bochs-2.2.6.tar.gz into a directory.
+The script pintos/src/misc/bochs-2.2.6-build.sh
+applies a number of patches contained in pintos/src/misc
+to the Bochs tree, then builds Bochs and installs it in a directory
+of your choice.
+Run this script without arguments to learn usage instructions.
+To use your bochs binary with pintos, make sure
+it is the one printed by which `bochs`; otherwise, modify
+your PATH accordingly.
+
+
+
+Of course, to get any good out of this you'll have to actually modify
+Bochs. Instructions for doing this are firmly out of the scope of
+this document. However, if you want to debug page faults as suggested
+above, a good place to start adding printf()s is
+BX_CPU_C::dtranslate_linear() in cpu/paging.cc.
+
+
+
+
+
+
+
D.8 Tips
+
+
+
+The page allocator in threads/palloc.c and the block allocator in
+threads/malloc.c clear all the bytes in memory to
+0xcc at time of free. Thus, if you see an attempt to
+dereference a pointer like 0xcccccccc, or some other reference to
+0xcc, there's a good chance you're trying to reuse a page that's
+already been freed. Also, byte 0xcc is the CPU opcode for "invoke
+interrupt 3," so if you see an error like Interrupt 0x03 (#BP
+Breakpoint Exception), then Pintos tried to execute code in a freed page or
+block.
+
+
+
+An assertion failure on the expression sec_no < d->capacity
+indicates that Pintos tried to access a file through an inode that has
+been closed and freed. Freeing an inode clears its starting sector
+number to 0xcccccccc, which is not a valid sector number for disks
+smaller than about 1.6 TB.
+
+
+
+Here are some tools that you might find useful while developing code.
+
+
+
+
+
+
+
E.1 Tags
+
+
+
+Tags are an index to the functions and global variables declared in a
+program. Many editors, including Emacs and vi, can use
+them. The Makefile in pintos/src produces Emacs-style
+tags with the command make TAGS or vi-style tags with
+make tags.
+
+
+
+In Emacs, use M-. to follow a tag in the current window,
+C-x 4 . in a new window, or C-x 5 . in a new frame. If
+your cursor is on a symbol name for any of those commands, it becomes
+the default target. If a tag name has multiple definitions, M-0
+M-. jumps to the next one. To jump back to where you were before
+you followed the last tag, use M-*.
+
+
+
+
+
+
+
E.2 cscope
+
+
+
+The cscope program also provides an index to functions and
+variables declared in a program. It has some features that tag
+facilities lack. Most notably, it can find all the points in a
+program at which a given function is called.
+
+
+
+The Makefile in pintos/src produces cscope
+indexes when it is invoked as make cscope. Once the index has
+been generated, run cscope from a shell command line; no
+command-line arguments are normally necessary. Then use the arrow
+keys to choose one of the search criteria listed near the bottom of
+the terminal, type in an identifier, and hit Enter.
+cscope will then display the matches in the upper part of
+the terminal. You may use the arrow keys to choose a particular
+match; if you then hit Enter, cscope will invoke the
+default system editor(7) and position the
+cursor on that match. To start a new search, type Tab. To exit
+cscope, type Ctrl-d.
+
+
+git is a version-control system. That is, you can use it to keep
+track of multiple versions of files. The idea is that you do some
+work on your code and test it, then check it into the version-control
+system. If you decide that the work you've done since your last
+check-in is no good, you can easily revert to the last checked-in
+version. Furthermore, you can retrieve any old version of your code
+as of some given day and time. The version control logs tell you who
+made changes and when.
+
GDB might tell you that
+schedule() doesn't exist, which is arguably a GDB bug.
+You can work around this by setting the breakpoint by filename and
+line number, e.g. break thread.c:ln where ln is
+the line number of the first declaration in schedule().
+
We
+will treat these terms as synonyms. There is no standard
+distinction between them, although Intel processor manuals make
+a minor distinction between them on 80x86.
+
This is because switch_threads() takes
+arguments on the stack and the 80x86 SVR4 calling convention
+requires the caller, not the called function, to remove them when the
+call is complete. See [ SysV-i386] chapter 3 for details.
+
Actually, virtual to physical translation on the
+80x86 architecture occurs via an intermediate "linear
+address," but Pintos (and most modern 80x86 OSes) set up the CPU
+so that linear and virtual addresses are one and the same. Thus, you
+can effectively ignore this CPU feature.
+
To be precise, GDB will stop
+only when running under Bochs. When running under QEMU, you must
+set a breakpoint in the page_fault function to stop execution
+when a page fault occurs. In that case, the btpagefault macro is
+unnecessary.
+
+
+
+
+This document was generated
+by on March, 6 2012
+using texi2html
+
+
+
+
diff --git a/doc/pintos_tour.pdf b/doc/pintos_tour.pdf
new file mode 100644
index 0000000..98ec371
Binary files /dev/null and b/doc/pintos_tour.pdf differ
diff --git a/doc/sample.tmpl b/doc/sample.tmpl
new file mode 100644
index 0000000..2d07635
--- /dev/null
+++ b/doc/sample.tmpl
@@ -0,0 +1,104 @@
+
+ +-----------------+
+ | CS 140 |
+ | SAMPLE PROJECT |
+ | DESIGN DOCUMENT |
+ +-----------------+
+
+---- GROUP ----
+
+Ben Pfaff
+
+---- PRELIMINARIES ----
+
+>> If you have any preliminary comments on your submission, notes for
+>> the TAs, or extra credit, please give them here.
+
+(This is a sample design document.)
+
+>> Please cite any offline or online sources you consulted while
+>> preparing your submission, other than the Pintos documentation,
+>> course text, and lecture notes.
+
+None.
+
+ JOIN
+ ====
+
+---- DATA STRUCTURES ----
+
+>> 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.
+
+A "latch" is a new synchronization primitive. Acquires block
+until the first release. Afterward, all ongoing and future
+acquires pass immediately.
+
+ /* Latch. */
+ struct latch
+ {
+ bool released; /* Released yet? */
+ struct lock monitor_lock; /* Monitor lock. */
+ struct condition rel_cond; /* Signaled when released. */
+ };
+
+Added to struct thread:
+
+ /* Members for implementing thread_join(). */
+ struct latch ready_to_die; /* Release when thread about to die. */
+ struct semaphore can_die; /* Up when thread allowed to die. */
+ struct list children; /* List of child threads. */
+ list_elem children_elem; /* Element of `children' list. */
+
+---- ALGORITHMS ----
+
+>> Briefly describe your implementation of thread_join() and how it
+>> interacts with thread termination.
+
+thread_join() finds the joined child on the thread's list of
+children and waits for the child to exit by acquiring the child's
+ready_to_die latch. When thread_exit() is called, the thread
+releases its ready_to_die latch, allowing the parent to continue.
+
+---- SYNCHRONIZATION ----
+
+>> Consider parent thread P with child thread C. How do you ensure
+>> proper synchronization and avoid race conditions when P calls wait(C)
+>> before C exits? After C exits? How do you ensure that all resources
+>> are freed in each case? How about when P terminates without waiting,
+>> before C exits? After C exits? Are there any special cases?
+
+C waits in thread_exit() for P to die before it finishes its own
+exit, using the can_die semaphore "down"ed by C and "up"ed by P as
+it exits. Regardless of whether whether C has terminated, there
+is no race on wait(C), because C waits for P's permission before
+it frees itself.
+
+Regardless of whether P waits for C, P still "up"s C's can_die
+semaphore when P dies, so C will always be freed. (However,
+freeing C's resources is delayed until P's death.)
+
+The initial thread is a special case because it has no parent to
+wait for it or to "up" its can_die semaphore. Therefore, its
+can_die semaphore is initialized to 1.
+
+---- RATIONALE ----
+
+>> Critique your design, pointing out advantages and disadvantages in
+>> your design choices.
+
+This design has the advantage of simplicity. Encapsulating most
+of the synchronization logic into a new "latch" structure
+abstracts what little complexity there is into a separate layer,
+making the design easier to reason about. Also, all the new data
+members are in `struct thread', with no need for any extra dynamic
+allocation, etc., that would require extra management code.
+
+On the other hand, this design is wasteful in that a child thread
+cannot free itself before its parent has terminated. A parent
+thread that creates a large number of short-lived child threads
+could unnecessarily exhaust kernel memory. This is probably
+acceptable for implementing kernel threads, but it may be a bad
+idea for use with user processes because of the larger number of
+resources that user processes tend to own.
diff --git a/doc/start.tmpl b/doc/start.tmpl
new file mode 100644
index 0000000..83b17ad
--- /dev/null
+++ b/doc/start.tmpl
@@ -0,0 +1,101 @@
+ +--------------------+
+ | OS Development |
+ | PROJECT 0: INTRO |
+ | DESIGN DOCUMENT |
+ +--------------------+
+
+---- GROUP ----
+
+>> Fill in the names and email addresses of your group members.
+
+FirstName LastName
+FirstName LastName
+FirstName LastName
+
+---- 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.
+
+ ALARM CLOCK
+ ===========
+
+---- 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: Briefly describe what happens in a call to timer_sleep(),
+>> including the effects of the timer interrupt handler.
+
+>> A3: What steps are taken to minimize the amount of time spent in
+>> the timer interrupt handler?
+
+---- SYNCHRONIZATION ----
+
+>> A4: How are race conditions avoided when multiple threads call
+>> timer_sleep() simultaneously?
+
+>> A5: How are race conditions avoided when a timer interrupt occurs
+>> during a call to timer_sleep()?
+
+---- RATIONALE ----
+
+>> A6: Why did you choose this design? In what ways is it superior to
+>> another design you considered?
+
+ ARGUMENT PASSING
+ ================
+
+---- 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: Briefly describe how you implemented argument parsing. How do
+>> you arrange for the elements of argv[] to be in the right order?
+>> How do you avoid overflowing the stack page?
+
+---- RATIONALE ----
+
+>> A3: Why does Pintos implement strtok_r() but not strtok()?
+
+>> A4: In Pintos, the kernel separates commands into a executable name
+>> and arguments. In Unix-like systems, the shell does this
+>> separation. Identify at least two advantages of the Unix approach.
+
+
+
+ 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?
diff --git a/doc/threads.tmpl b/doc/threads.tmpl
new file mode 100644
index 0000000..c3df5fe
--- /dev/null
+++ b/doc/threads.tmpl
@@ -0,0 +1,82 @@
+ +--------------------+
+ | CS 140 |
+ | PROJECT 1: THREADS |
+ | DESIGN DOCUMENT |
+ +--------------------+
+
+---- GROUP ----
+
+>> Fill in the names and email addresses of your group members.
+
+FirstName LastName
+FirstName LastName
+FirstName LastName
+
+---- 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.
+
+
+ PRIORITY SCHEDULING
+ ===================
+
+---- 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.
+
+>> B2: Explain the data structure used to track priority donation.
+>> Use ASCII art to diagram a nested donation. (Alternately, submit a
+>> .png file.)
+
+---- ALGORITHMS ----
+
+>> B3: How do you ensure that the highest priority thread waiting for
+>> a lock, semaphore, or condition variable wakes up first?
+
+>> B4: Describe the sequence of events when a call to lock_acquire()
+>> causes a priority donation. How is nested donation handled?
+
+>> B5: Describe the sequence of events when lock_release() is called
+>> on a lock that a higher-priority thread is waiting for.
+
+---- SYNCHRONIZATION ----
+
+>> B6: Describe a potential race in thread_set_priority() and explain
+>> how your implementation avoids it. Can you use a lock to avoid
+>> this race?
+
+---- RATIONALE ----
+
+>> B7: Why did you choose this design? In what ways is it superior to
+>> another design you considered?
+
+ 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?
diff --git a/doc/vm.tmpl b/doc/vm.tmpl
new file mode 100644
index 0000000..82b0806
--- /dev/null
+++ b/doc/vm.tmpl
@@ -0,0 +1,81 @@
+ +---------------------------+
+ | CS 140 |
+ | PROJECT 3: VIRTUAL MEMORY |
+ | DESIGN DOCUMENT |
+ +---------------------------+
+
+---- GROUP ----
+
+>> Fill in the names and email addresses of your group members.
+
+FirstName LastName
+FirstName LastName
+FirstName LastName
+
+---- 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
+ =====================
+
+TODO
+
+ STACK GROWTH
+ ============
+TODO
+
+ 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.
+
+>> 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?
diff --git a/env.sh b/env.sh
new file mode 100644
index 0000000..99fb242
--- /dev/null
+++ b/env.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+export PATH="$PATH:$(dirname "${BASH_SOURCE[0]}")/pintos-progos/utils"
+export PATH="/opt/bochs/bin:$PATH"
diff --git a/gdb-macros b/gdb-macros
new file mode 100644
index 0000000..a0b68c3
--- /dev/null
+++ b/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 , 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/pintos-progos/LICENSE b/pintos-progos/LICENSE
new file mode 100644
index 0000000..8702541
--- /dev/null
+++ b/pintos-progos/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/pintos-progos/Make.config b/pintos-progos/Make.config
new file mode 100644
index 0000000..f00e8fc
--- /dev/null
+++ b/pintos-progos/Make.config
@@ -0,0 +1,52 @@
+# -*- 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
+CPPFLAGS = -nostdinc -I$(SRCDIR) -I$(SRCDIR)/lib
+ASFLAGS = -Wa,--gstabs
+LDFLAGS =
+DEPS = -MMD -MF $(@:.o=.d)
+
+# Turn off -fstack-protector, which we don't support.
+CFLAGS += -fno-stack-protector
+
+# Turn off --build-id in the linker, which confuses the Pintos loader.
+LDFLAGS += -Wl,--build-id=none
+
+%.o: %.c
+ $(CC) -c $< -o $@ $(CFLAGS) $(CPPFLAGS) $(WARNINGS) $(DEFINES) $(DEPS)
+
+%.o: %.S
+ $(CC) -c $< -o $@ $(ASFLAGS) $(CPPFLAGS) $(DEFINES) $(DEPS)
diff --git a/pintos-progos/Makefile b/pintos-progos/Makefile
new file mode 100644
index 0000000..3ba9194
--- /dev/null
+++ b/pintos-progos/Makefile
@@ -0,0 +1,29 @@
+BUILD_SUBDIRS = threads userprog vm filesys intro
+
+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/pintos-progos/Makefile.build b/pintos-progos/Makefile.build
new file mode 100644
index 0000000..e997d27
--- /dev/null
+++ b/pintos-progos/Makefile.build
@@ -0,0 +1,109 @@
+# -*- 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.
+
+# No virtual memory code yet.
+#vm_SRC = vm/file.c # Some file.
+
+# 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.
+
+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/pintos-progos/Makefile.kernel b/pintos-progos/Makefile.kernel
new file mode 100644
index 0000000..162a411
--- /dev/null
+++ b/pintos-progos/Makefile.kernel
@@ -0,0 +1,20 @@
+# -*- 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/pintos-progos/Makefile.userprog b/pintos-progos/Makefile.userprog
new file mode 100644
index 0000000..0df391a
--- /dev/null
+++ b/pintos-progos/Makefile.userprog
@@ -0,0 +1,52 @@
+# -*- 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/pintos-progos/devices/block.c b/pintos-progos/devices/block.c
new file mode 100644
index 0000000..a3acec1
--- /dev/null
+++ b/pintos-progos/devices/block.c
@@ -0,0 +1,223 @@
+#include "devices/block.h"
+#include
+#include
+#include
+#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/pintos-progos/devices/block.h b/pintos-progos/devices/block.h
new file mode 100644
index 0000000..21732d6
--- /dev/null
+++ b/pintos-progos/devices/block.h
@@ -0,0 +1,74 @@
+#ifndef DEVICES_BLOCK_H
+#define DEVICES_BLOCK_H
+
+#include
+#include
+
+/* 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/pintos-progos/devices/ide.c b/pintos-progos/devices/ide.c
new file mode 100644
index 0000000..2cc0292
--- /dev/null
+++ b/pintos-progos/devices/ide.c
@@ -0,0 +1,527 @@
+#include "devices/ide.h"
+#include
+#include
+#include
+#include
+#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/pintos-progos/devices/ide.h b/pintos-progos/devices/ide.h
new file mode 100644
index 0000000..b35da5e
--- /dev/null
+++ b/pintos-progos/devices/ide.h
@@ -0,0 +1,6 @@
+#ifndef DEVICES_IDE_H
+#define DEVICES_IDE_H
+
+void ide_init (void);
+
+#endif /* devices/ide.h */
diff --git a/pintos-progos/devices/input.c b/pintos-progos/devices/input.c
new file mode 100644
index 0000000..4a12160
--- /dev/null
+++ b/pintos-progos/devices/input.c
@@ -0,0 +1,52 @@
+#include "devices/input.h"
+#include
+#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/pintos-progos/devices/input.h b/pintos-progos/devices/input.h
new file mode 100644
index 0000000..a2f50e9
--- /dev/null
+++ b/pintos-progos/devices/input.h
@@ -0,0 +1,12 @@
+#ifndef DEVICES_INPUT_H
+#define DEVICES_INPUT_H
+
+#include
+#include
+
+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/pintos-progos/devices/intq.c b/pintos-progos/devices/intq.c
new file mode 100644
index 0000000..40b23ae
--- /dev/null
+++ b/pintos-progos/devices/intq.c
@@ -0,0 +1,114 @@
+#include "devices/intq.h"
+#include
+#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/pintos-progos/devices/intq.h b/pintos-progos/devices/intq.h
new file mode 100644
index 0000000..2312b12
--- /dev/null
+++ b/pintos-progos/devices/intq.h
@@ -0,0 +1,43 @@
+#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/pintos-progos/devices/kbd.c b/pintos-progos/devices/kbd.c
new file mode 100644
index 0000000..fcc82be
--- /dev/null
+++ b/pintos-progos/devices/kbd.c
@@ -0,0 +1,213 @@
+#include "devices/kbd.h"
+#include
+#include
+#include
+#include
+#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/pintos-progos/devices/kbd.h b/pintos-progos/devices/kbd.h
new file mode 100644
index 0000000..ed9c06b
--- /dev/null
+++ b/pintos-progos/devices/kbd.h
@@ -0,0 +1,9 @@
+#ifndef DEVICES_KBD_H
+#define DEVICES_KBD_H
+
+#include
+
+void kbd_init (void);
+void kbd_print_stats (void);
+
+#endif /* devices/kbd.h */
diff --git a/pintos-progos/devices/partition.c b/pintos-progos/devices/partition.c
new file mode 100644
index 0000000..7e97332
--- /dev/null
+++ b/pintos-progos/devices/partition.c
@@ -0,0 +1,324 @@
+#include "devices/partition.h"
+#include
+#include
+#include
+#include
+#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/pintos-progos/devices/partition.h b/pintos-progos/devices/partition.h
new file mode 100644
index 0000000..47fea4d
--- /dev/null
+++ b/pintos-progos/devices/partition.h
@@ -0,0 +1,8 @@
+#ifndef DEVICES_PARTITION_H
+#define DEVICES_PARTITION_H
+
+struct block;
+
+void partition_scan (struct block *);
+
+#endif /* devices/partition.h */
diff --git a/pintos-progos/devices/pit.c b/pintos-progos/devices/pit.c
new file mode 100644
index 0000000..bfb1889
--- /dev/null
+++ b/pintos-progos/devices/pit.c
@@ -0,0 +1,83 @@
+#include "devices/pit.h"
+#include
+#include
+#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/pintos-progos/devices/pit.h b/pintos-progos/devices/pit.h
new file mode 100644
index 0000000..dff36ae
--- /dev/null
+++ b/pintos-progos/devices/pit.h
@@ -0,0 +1,8 @@
+#ifndef DEVICES_PIT_H
+#define DEVICES_PIT_H
+
+#include
+
+void pit_configure_channel (int channel, int mode, int frequency);
+
+#endif /* devices/pit.h */
diff --git a/pintos-progos/devices/rtc.c b/pintos-progos/devices/rtc.c
new file mode 100644
index 0000000..d99eb46
--- /dev/null
+++ b/pintos-progos/devices/rtc.c
@@ -0,0 +1,112 @@
+#include "devices/rtc.h"
+#include
+#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 + (year - 1) / 4) * 24 * 60 * 60;
+ for (i = 1; i <= mon; i++)
+ time += days_per_month[i - 1] * 24 * 60 * 60;
+ if (mon > 2 && year % 4 == 0)
+ 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/pintos-progos/devices/rtc.h b/pintos-progos/devices/rtc.h
new file mode 100644
index 0000000..96a822f
--- /dev/null
+++ b/pintos-progos/devices/rtc.h
@@ -0,0 +1,8 @@
+#ifndef RTC_H
+#define RTC_H
+
+typedef unsigned long time_t;
+
+time_t rtc_get_time (void);
+
+#endif
diff --git a/pintos-progos/devices/serial.c b/pintos-progos/devices/serial.c
new file mode 100644
index 0000000..df770a7
--- /dev/null
+++ b/pintos-progos/devices/serial.c
@@ -0,0 +1,228 @@
+#include "devices/serial.h"
+#include
+#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/pintos-progos/devices/serial.h b/pintos-progos/devices/serial.h
new file mode 100644
index 0000000..6e04778
--- /dev/null
+++ b/pintos-progos/devices/serial.h
@@ -0,0 +1,11 @@
+#ifndef DEVICES_SERIAL_H
+#define DEVICES_SERIAL_H
+
+#include
+
+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/pintos-progos/devices/shutdown.c b/pintos-progos/devices/shutdown.c
new file mode 100644
index 0000000..7ff9a95
--- /dev/null
+++ b/pintos-progos/devices/shutdown.c
@@ -0,0 +1,131 @@
+#include "devices/shutdown.h"
+#include
+#include
+#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 ();
+
+ /* 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);
+
+ /* 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/pintos-progos/devices/shutdown.h b/pintos-progos/devices/shutdown.h
new file mode 100644
index 0000000..dc4f942
--- /dev/null
+++ b/pintos-progos/devices/shutdown.h
@@ -0,0 +1,19 @@
+#ifndef DEVICES_SHUTDOWN_H
+#define DEVICES_SHUTDOWN_H
+
+#include
+
+/* 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/pintos-progos/devices/speaker.c b/pintos-progos/devices/speaker.c
new file mode 100644
index 0000000..5052005
--- /dev/null
+++ b/pintos-progos/devices/speaker.c
@@ -0,0 +1,68 @@
+#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/pintos-progos/devices/speaker.h b/pintos-progos/devices/speaker.h
new file mode 100644
index 0000000..98cef7b
--- /dev/null
+++ b/pintos-progos/devices/speaker.h
@@ -0,0 +1,8 @@
+#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/pintos-progos/devices/timer.c b/pintos-progos/devices/timer.c
new file mode 100644
index 0000000..befaaae
--- /dev/null
+++ b/pintos-progos/devices/timer.c
@@ -0,0 +1,246 @@
+#include "devices/timer.h"
+#include
+#include
+#include
+#include
+#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 (high_bit | 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/pintos-progos/devices/timer.h b/pintos-progos/devices/timer.h
new file mode 100644
index 0000000..cd3d6bb
--- /dev/null
+++ b/pintos-progos/devices/timer.h
@@ -0,0 +1,29 @@
+#ifndef DEVICES_TIMER_H
+#define DEVICES_TIMER_H
+
+#include
+#include
+
+/* 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/pintos-progos/devices/vga.c b/pintos-progos/devices/vga.c
new file mode 100644
index 0000000..f421b61
--- /dev/null
+++ b/pintos-progos/devices/vga.c
@@ -0,0 +1,172 @@
+#include "devices/vga.h"
+#include
+#include
+#include
+#include
+#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/pintos-progos/devices/vga.h b/pintos-progos/devices/vga.h
new file mode 100644
index 0000000..59690fb
--- /dev/null
+++ b/pintos-progos/devices/vga.h
@@ -0,0 +1,6 @@
+#ifndef DEVICES_VGA_H
+#define DEVICES_VGA_H
+
+void vga_putc (int);
+
+#endif /* devices/vga.h */
diff --git a/pintos-progos/examples/.gitignore b/pintos-progos/examples/.gitignore
new file mode 100644
index 0000000..a9e09d7
--- /dev/null
+++ b/pintos-progos/examples/.gitignore
@@ -0,0 +1,19 @@
+cat
+cmp
+cp
+echo
+halt
+hex-dump
+ls
+mcat
+mcp
+mkdir
+pwd
+rm
+shell
+bubsort
+insult
+lineup
+matmult
+recursor
+*.d
diff --git a/pintos-progos/examples/Makefile b/pintos-progos/examples/Makefile
new file mode 100644
index 0000000..f773950
--- /dev/null
+++ b/pintos-progos/examples/Makefile
@@ -0,0 +1,36 @@
+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 hello hex-dump ls mcat mcp mkdir pwd rm shell \
+ bubsort insult lineup matmult recursor test
+
+# 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
+hello_SRC = hello.c
+hex-dump_SRC = hex-dump.c
+insult_SRC = insult.c
+lineup_SRC = lineup.c
+ls_SRC = ls.c
+recursor_SRC = recursor.c
+rm_SRC = rm.c
+test_SRC = test.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/pintos-progos/examples/bubsort.c b/pintos-progos/examples/bubsort.c
new file mode 100644
index 0000000..343219e
--- /dev/null
+++ b/pintos-progos/examples/bubsort.c
@@ -0,0 +1,38 @@
+/* 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
+
+/* 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/pintos-progos/examples/cat.c b/pintos-progos/examples/cat.c
new file mode 100644
index 0000000..c8d229d
--- /dev/null
+++ b/pintos-progos/examples/cat.c
@@ -0,0 +1,34 @@
+/* cat.c
+
+ Prints files specified on command line to the console. */
+
+#include
+#include
+
+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/pintos-progos/examples/cmp.c b/pintos-progos/examples/cmp.c
new file mode 100644
index 0000000..94b406d
--- /dev/null
+++ b/pintos-progos/examples/cmp.c
@@ -0,0 +1,68 @@
+/* cat.c
+
+ Compares two files. */
+
+#include
+#include
+
+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/pintos-progos/examples/cp.c b/pintos-progos/examples/cp.c
new file mode 100644
index 0000000..86a5cd7
--- /dev/null
+++ b/pintos-progos/examples/cp.c
@@ -0,0 +1,55 @@
+/* cat.c
+
+Copies one file to another. */
+
+#include
+#include
+
+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/pintos-progos/examples/echo.c b/pintos-progos/examples/echo.c
new file mode 100644
index 0000000..1b136f2
--- /dev/null
+++ b/pintos-progos/examples/echo.c
@@ -0,0 +1,14 @@
+#include
+#include
+
+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/pintos-progos/examples/halt.c b/pintos-progos/examples/halt.c
new file mode 100644
index 0000000..bad7250
--- /dev/null
+++ b/pintos-progos/examples/halt.c
@@ -0,0 +1,14 @@
+/* halt.c
+
+ Simple program to test whether running a user program works.
+
+ Just invokes a system call that shuts down the OS. */
+
+#include
+
+int
+main (void)
+{
+ halt ();
+ /* not reached */
+}
diff --git a/pintos-progos/examples/hello.c b/pintos-progos/examples/hello.c
new file mode 100644
index 0000000..a12fd94
--- /dev/null
+++ b/pintos-progos/examples/hello.c
@@ -0,0 +1,9 @@
+#include
+#include
+
+int
+main (int argc, char **argv)
+{
+ printf ("Hello World :)\n");
+ return EXIT_SUCCESS;
+}
diff --git a/pintos-progos/examples/hex-dump.c b/pintos-progos/examples/hex-dump.c
new file mode 100644
index 0000000..ee313f2
--- /dev/null
+++ b/pintos-progos/examples/hex-dump.c
@@ -0,0 +1,35 @@
+/* hex-dump.c
+
+ Prints files specified on command line to the console in hex. */
+
+#include
+#include
+
+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/pintos-progos/examples/insult.c b/pintos-progos/examples/insult.c
new file mode 100644
index 0000000..98c4e6a
--- /dev/null
+++ b/pintos-progos/examples/insult.c
@@ -0,0 +1,369 @@
+/* Insult.c
+
+ This is a version of the famous CS 107 random sentence
+ generator. I wrote a program that reads a grammar definition
+ file and writes a C file containing that grammar as hard code
+ static C strings. Thus the majority of the code below in
+ machine generated and totally unreadable. The arrays created
+ are specially designed to make generating the sentences as
+ easy as possible.
+
+ Originally by Greg Hutchins, March 1998.
+ Modified by Ben Pfaff for Pintos, Sept 2004. */
+char *start[] =
+ { "You", "1", "5", ".", "May", "13", ".", "With", "the", "19", "of", "18",
+",", "may", "13", "."
+};
+char startLoc[] = { 3, 0, 4, 7, 16 };
+char *adj[] = { "3", "4", "2", ",", "1" };
+char adjLoc[] = { 3, 0, 1, 2, 5 };
+char *adj3[] = { "3", "4" };
+char adj3Loc[] = { 2, 0, 1, 2 };
+char *adj1[] =
+ { "lame", "dried", "up", "par-broiled", "bloated", "half-baked", "spiteful",
+"egotistical", "ungrateful", "stupid", "moronic", "fat", "ugly", "puny", "pitiful",
+"insignificant", "blithering", "repulsive", "worthless", "blundering", "retarded",
+"useless", "obnoxious", "low-budget", "assinine", "neurotic", "subhuman", "crochety",
+"indescribable", "contemptible", "unspeakable", "sick", "lazy", "good-for-nothing",
+"slutty", "mentally-deficient", "creepy", "sloppy", "dismal", "pompous", "pathetic",
+"friendless", "revolting", "slovenly", "cantankerous", "uncultured", "insufferable",
+"gross", "unkempt", "defective", "crumby"
+};
+char adj1Loc[] =
+ { 50, 0, 1, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20,
+21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42,
+43, 44, 45, 46, 47, 48, 49, 50, 51 };
+char *adj2[] =
+ { "putrefied", "festering", "funky", "moldy", "leprous", "curdled", "fetid",
+"slimy", "crusty", "sweaty", "damp", "deranged", "smelly", "stenchy", "malignant",
+"noxious", "grimy", "reeky", "nasty", "mutilated", "sloppy", "gruesome", "grisly",
+"sloshy", "wormy", "mealy", "spoiled", "contaminated", "rancid", "musty",
+"fly-covered", "moth-eaten", "decaying", "decomposed", "freeze-dried", "defective",
+"petrified", "rotting", "scabrous", "hirsute"
+};
+char adj2Loc[] =
+ { 40, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
+20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40 };
+char *name[] =
+ { "10", ",", "bad", "excuse", "for", "6", ",", "6", "for", "brains", ",",
+"4", "11", "8", "for", "brains", "offspring", "of", "a", "motherless", "10", "7", "6",
+"7", "4", "11", "8"
+};
+char nameLoc[] = { 7, 0, 1, 6, 10, 16, 21, 23, 27 };
+char *stuff[] =
+ { "shit", "toe", "jam", "filth", "puss", "earwax", "leaf", "clippings",
+"bat", "guano", "mucus", "fungus", "mung", "refuse", "earwax", "spittoon", "spittle",
+"phlegm"
+};
+char stuffLoc[] = { 14, 0, 1, 3, 4, 5, 6, 8, 10, 11, 12, 13, 14, 15, 17, 18 };
+char *noun_and_prep[] =
+ { "bit", "of", "piece", "of", "vat", "of", "lump", "of", "crock", "of",
+"ball", "of", "tub", "of", "load", "of", "bucket", "of", "mound", "of", "glob", "of", "bag",
+"of", "heap", "of", "mountain", "of", "load", "of", "barrel", "of", "sack", "of", "blob", "of",
+"pile", "of", "truckload", "of", "vat", "of"
+};
+char noun_and_prepLoc[] =
+ { 21, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36,
+38, 40, 42 };
+char *organics[] =
+ { "droppings", "mung", "zits", "puckies", "tumors", "cysts", "tumors",
+"livers", "froth", "parts", "scabs", "guts", "entrails", "blubber", "carcuses", "gizards",
+"9"
+};
+char organicsLoc[] =
+ { 17, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 };
+char *body_parts[] =
+ { "kidneys", "genitals", "buttocks", "earlobes", "innards", "feet"
+};
+char body_partsLoc[] = { 6, 0, 1, 2, 3, 4, 5, 6 };
+char *noun[] =
+ { "pop", "tart", "warthog", "twinkie", "barnacle", "fondue", "pot",
+"cretin", "fuckwad", "moron", "ass", "neanderthal", "nincompoop", "simpleton", "11"
+};
+char nounLoc[] = { 13, 0, 2, 3, 4, 5, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+char *animal[] =
+ { "donkey", "llama", "dingo", "lizard", "gekko", "lemur", "moose", "camel",
+"goat", "eel"
+};
+char animalLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+char *good_verb[] =
+ { "love", "cuddle", "fondle", "adore", "smooch", "hug", "caress", "worship",
+"look", "at", "touch"
+};
+char good_verbLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11 };
+char *curse[] =
+ { "14", "20", "23", "14", "17", "20", "23", "14", "find", "your", "9",
+"suddenly", "delectable", "14", "and", "14", "seek", "a", "battleground", "23"
+};
+char curseLoc[] = { 4, 0, 3, 7, 13, 20 };
+char *afflictors[] =
+ { "15", "21", "15", "21", "15", "21", "15", "21", "a", "22", "Rush",
+"Limbaugh", "the", "hosts", "of", "Hades"
+};
+char afflictorsLoc[] = { 6, 0, 2, 4, 6, 8, 12, 16 };
+char *quantity[] =
+ { "a", "4", "hoard", "of", "a", "4", "pack", "of", "a", "truckload", "of",
+"a", "swarm", "of", "many", "an", "army", "of", "a", "4", "heard", "of", "a", "4",
+"platoon", "of", "a", "4", "and", "4", "group", "of", "16"
+};
+char quantityLoc[] = { 10, 0, 4, 8, 11, 14, 15, 18, 22, 26, 32, 33 };
+char *numbers[] =
+ { "a", "thousand", "three", "million", "ninty-nine", "nine-hundred,",
+"ninty-nine", "forty-two", "a", "gazillion", "sixty-eight", "times", "thirty-three"
+};
+char numbersLoc[] = { 7, 0, 2, 4, 5, 7, 8, 10, 13 };
+char *adv[] =
+ { "viciously", "manicly", "merrily", "happily", ",", "with", "the", "19",
+"of", "18", ",", "gleefully", ",", "with", "much", "ritualistic", "celebration", ",",
+"franticly"
+};
+char advLoc[] = { 8, 0, 1, 2, 3, 4, 11, 12, 18, 19 };
+char *metaphor[] =
+ { "an", "irate", "manticore", "Thor's", "belch", "Alah's", "fist", "16",
+"titans", "a", "particularly", "vicious", "she-bear", "in", "the", "midst", "of", "her",
+"menstrual", "cycle", "a", "pissed-off", "Jabberwock"
+};
+char metaphorLoc[] = { 6, 0, 3, 5, 7, 9, 20, 23 };
+char *force[] = { "force", "fury", "power", "rage" };
+char forceLoc[] = { 4, 0, 1, 2, 3, 4 };
+char *bad_action[] =
+ { "spit", "shimmy", "slobber", "find", "refuge", "find", "shelter", "dance",
+"retch", "vomit", "defecate", "erect", "a", "strip", "mall", "build", "a", "26", "have", "a",
+"religious", "experience", "discharge", "bodily", "waste", "fart", "dance", "drool",
+"lambada", "spill", "16", "rusty", "tacks", "bite", "you", "sneeze", "sing", "16",
+"campfire", "songs", "smite", "you", "16", "times", "construct", "a", "new", "home", "throw",
+"a", "party", "procreate"
+};
+char bad_actionLoc[] =
+ { 25, 0, 1, 2, 3, 5, 7, 8, 9, 10, 11, 15, 18, 22, 25, 26, 27, 28, 29, 33,
+35, 36, 40, 44, 48, 51, 52 };
+char *beasties[] =
+ { "yaks", "22", "maggots", "22", "cockroaches", "stinging", "scorpions",
+"fleas", "22", "weasels", "22", "gnats", "South", "American", "killer", "bees", "spiders",
+"4", "monkeys", "22", "wiener-dogs", "22", "rats", "22", "wolverines", "4", ",", "22",
+"pit-fiends"
+};
+char beastiesLoc[] =
+ { 14, 0, 1, 3, 5, 7, 8, 10, 12, 16, 17, 19, 21, 23, 25, 29 };
+char *condition[] =
+ { "frothing", "manic", "crazed", "plague-ridden", "disease-carrying",
+"biting", "rabid", "blood-thirsty", "ravaging", "slavering"
+};
+char conditionLoc[] = { 10, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
+char *place[] =
+ { "in", "24", "25", "upon", "your", "mother's", "grave", "on", "24", "best",
+"rug", "in", "the", "26", "you", "call", "home", "upon", "your", "heinie"
+};
+char placeLoc[] = { 5, 0, 3, 7, 11, 17, 20 };
+char *relation[] =
+ { "your", "your", "your", "your", "father's", "your", "mother's", "your",
+"grandma's"
+};
+char relationLoc[] = { 6, 0, 1, 2, 3, 5, 7, 9 };
+char *in_something[] =
+ { "entrails", "anal", "cavity", "shoes", "house", "pantry", "general",
+"direction", "pants", "bed"
+};
+char in_somethingLoc[] = { 8, 0, 1, 3, 4, 5, 6, 8, 9, 10 };
+char *bad_place[] =
+ { "rat", "hole", "sewer", "toxic", "dump", "oil", "refinery", "landfill",
+"porto-pottie"
+};
+char bad_placeLoc[] = { 6, 0, 2, 3, 5, 7, 8, 9 };
+char **daGrammar[27];
+char *daGLoc[27];
+
+static void
+init_grammar (void)
+{
+ daGrammar[0] = start;
+ daGLoc[0] = startLoc;
+ daGrammar[1] = adj;
+ daGLoc[1] = adjLoc;
+ daGrammar[2] = adj3;
+ daGLoc[2] = adj3Loc;
+ daGrammar[3] = adj1;
+ daGLoc[3] = adj1Loc;
+ daGrammar[4] = adj2;
+ daGLoc[4] = adj2Loc;
+ daGrammar[5] = name;
+ daGLoc[5] = nameLoc;
+ daGrammar[6] = stuff;
+ daGLoc[6] = stuffLoc;
+ daGrammar[7] = noun_and_prep;
+ daGLoc[7] = noun_and_prepLoc;
+ daGrammar[8] = organics;
+ daGLoc[8] = organicsLoc;
+ daGrammar[9] = body_parts;
+ daGLoc[9] = body_partsLoc;
+ daGrammar[10] = noun;
+ daGLoc[10] = nounLoc;
+ daGrammar[11] = animal;
+ daGLoc[11] = animalLoc;
+ daGrammar[12] = good_verb;
+ daGLoc[12] = good_verbLoc;
+ daGrammar[13] = curse;
+ daGLoc[13] = curseLoc;
+ daGrammar[14] = afflictors;
+ daGLoc[14] = afflictorsLoc;
+ daGrammar[15] = quantity;
+ daGLoc[15] = quantityLoc;
+ daGrammar[16] = numbers;
+ daGLoc[16] = numbersLoc;
+ daGrammar[17] = adv;
+ daGLoc[17] = advLoc;
+ daGrammar[18] = metaphor;
+ daGLoc[18] = metaphorLoc;
+ daGrammar[19] = force;
+ daGLoc[19] = forceLoc;
+ daGrammar[20] = bad_action;
+ daGLoc[20] = bad_actionLoc;
+ daGrammar[21] = beasties;
+ daGLoc[21] = beastiesLoc;
+ daGrammar[22] = condition;
+ daGLoc[22] = conditionLoc;
+ daGrammar[23] = place;
+ daGLoc[23] = placeLoc;
+ daGrammar[24] = relation;
+ daGLoc[24] = relationLoc;
+ daGrammar[25] = in_something;
+ daGLoc[25] = in_somethingLoc;
+ daGrammar[26] = bad_place;
+ daGLoc[26] = bad_placeLoc;
+}
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+void expand (int num, char **grammar[], char *location[], int handle);
+
+static void
+usage (int ret_code, const char *message, ...) PRINTF_FORMAT (2, 3);
+
+static void
+usage (int ret_code, const char *message, ...)
+{
+ va_list args;
+
+ if (message != NULL)
+ {
+ va_start (args, message);
+ vprintf (message, args);
+ va_end (args);
+ }
+
+ printf ("\n"
+ "Usage: insult [OPTION]...\n"
+ "Prints random insults to screen.\n\n"
+ " -h: this help message\n"
+ " -s : set the random seed (default 4951)\n"
+ " -n : choose number of insults (default 4)\n"
+ " -f : redirect output to \n");
+
+ exit (ret_code);
+}
+
+int
+main (int argc, char *argv[])
+{
+ int sentence_cnt, new_seed, i, file_flag, sent_flag, seed_flag;
+ int handle;
+
+ new_seed = 4951;
+ sentence_cnt = 4;
+ file_flag = 0;
+ seed_flag = 0;
+ sent_flag = 0;
+ handle = STDOUT_FILENO;
+
+ for (i = 1; i < argc; i++)
+ {
+ if (strcmp (argv[1], "-h") == 0)
+ usage (0, NULL);
+ else if (strcmp (argv[i], "-s") == 0)
+ {
+ if (seed_flag++)
+ usage (-1, "Can't have more than one seed");
+ if (++i >= argc)
+ usage (-1, "Missing value for -s");
+ new_seed = atoi (argv[i]);
+ }
+ else if (strcmp (argv[i], "-n") == 0)
+ {
+ if (sent_flag++)
+ usage (-1, "Can't have more than one sentence option");
+ if (++i >= argc)
+ usage (-1, "Missing value for -n");
+ sentence_cnt = atoi (argv[i]);
+ if (sentence_cnt < 1)
+ usage (-1, "Must have at least one sentence");
+ }
+ else if (strcmp (argv[i], "-f") == 0)
+ {
+ if (file_flag++)
+ usage (-1, "Can't have more than one output file");
+ if (++i >= argc)
+ usage (-1, "Missing value for -f");
+
+ /* Because files have fixed length in the basic Pintos
+ file system, the 0 argument means that this option
+ will not be useful until project 4 is
+ implemented. */
+ create (argv[i], 0);
+ handle = open (argv[i]);
+ if (handle < 0)
+ {
+ printf ("%s: open failed\n", argv[i]);
+ return EXIT_FAILURE;
+ }
+ }
+ else
+ usage (-1, "Unrecognized flag");
+ }
+
+ init_grammar ();
+
+ random_init (new_seed);
+ hprintf (handle, "\n");
+
+ for (i = 0; i < sentence_cnt; i++)
+ {
+ hprintf (handle, "\n");
+ expand (0, daGrammar, daGLoc, handle);
+ hprintf (handle, "\n\n");
+ }
+
+ if (file_flag)
+ close (handle);
+
+ return EXIT_SUCCESS;
+}
+
+void
+expand (int num, char **grammar[], char *location[], int handle)
+{
+ char *word;
+ int i, which, listStart, listEnd;
+
+ which = random_ulong () % location[num][0] + 1;
+ listStart = location[num][which];
+ listEnd = location[num][which + 1];
+ for (i = listStart; i < listEnd; i++)
+ {
+ word = grammar[num][i];
+ if (!isdigit (*word))
+ {
+ if (!ispunct (*word))
+ hprintf (handle, " ");
+ hprintf (handle, "%s", word);
+ }
+ else
+ expand (atoi (word), grammar, location, handle);
+ }
+
+}
diff --git a/pintos-progos/examples/lib/.gitignore b/pintos-progos/examples/lib/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/pintos-progos/examples/lib/.gitignore
@@ -0,0 +1 @@
+*.d
diff --git a/pintos-progos/examples/lib/user/.dummy b/pintos-progos/examples/lib/user/.dummy
new file mode 100644
index 0000000..e69de29
diff --git a/pintos-progos/examples/lib/user/.gitignore b/pintos-progos/examples/lib/user/.gitignore
new file mode 100644
index 0000000..a438335
--- /dev/null
+++ b/pintos-progos/examples/lib/user/.gitignore
@@ -0,0 +1 @@
+*.d
diff --git a/pintos-progos/examples/lineup.c b/pintos-progos/examples/lineup.c
new file mode 100644
index 0000000..60402d0
--- /dev/null
+++ b/pintos-progos/examples/lineup.c
@@ -0,0 +1,46 @@
+/* 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
+#include
+#include
+
+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/pintos-progos/examples/ls.c b/pintos-progos/examples/ls.c
new file mode 100644
index 0000000..fbe27a1
--- /dev/null
+++ b/pintos-progos/examples/ls.c
@@ -0,0 +1,90 @@
+/* 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
+#include
+#include
+
+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/pintos-progos/examples/matmult.c b/pintos-progos/examples/matmult.c
new file mode 100644
index 0000000..4f0615f
--- /dev/null
+++ b/pintos-progos/examples/matmult.c
@@ -0,0 +1,57 @@
+/* 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
+#include
+
+/* 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/pintos-progos/examples/mcat.c b/pintos-progos/examples/mcat.c
new file mode 100644
index 0000000..7b39760
--- /dev/null
+++ b/pintos-progos/examples/mcat.c
@@ -0,0 +1,45 @@
+/* mcat.c
+
+ Prints files specified on command line to the console, using
+ mmap. */
+
+#include
+#include
+
+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/pintos-progos/examples/mcp.c b/pintos-progos/examples/mcp.c
new file mode 100644
index 0000000..6091dc8
--- /dev/null
+++ b/pintos-progos/examples/mcp.c
@@ -0,0 +1,68 @@
+/* mcp.c
+
+ Copies one file to another, using mmap. */
+
+#include
+#include
+#include
+
+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/pintos-progos/examples/mkdir.c b/pintos-progos/examples/mkdir.c
new file mode 100644
index 0000000..7ddbc3f
--- /dev/null
+++ b/pintos-progos/examples/mkdir.c
@@ -0,0 +1,24 @@
+/* mkdir.c
+
+ Creates a directory. */
+
+#include
+#include
+
+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/pintos-progos/examples/pwd.c b/pintos-progos/examples/pwd.c
new file mode 100644
index 0000000..d2305cf
--- /dev/null
+++ b/pintos-progos/examples/pwd.c
@@ -0,0 +1,152 @@
+/* pwd.c
+
+ Prints the absolute name of the present working directory. */
+
+#include
+#include
+#include
+#include
+
+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/pintos-progos/examples/recursor.c b/pintos-progos/examples/recursor.c
new file mode 100644
index 0000000..79c784a
--- /dev/null
+++ b/pintos-progos/examples/recursor.c
@@ -0,0 +1,34 @@
+#include
+#include
+#include
+
+int
+main (int argc, char *argv[])
+{
+ char buffer[128];
+ pid_t pid;
+ int retval = 0;
+
+ if (argc != 4)
+ {
+ printf ("usage: recursor \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/pintos-progos/examples/rm.c b/pintos-progos/examples/rm.c
new file mode 100644
index 0000000..0db7f7b
--- /dev/null
+++ b/pintos-progos/examples/rm.c
@@ -0,0 +1,21 @@
+/* rm.c
+
+ Removes files specified on command line. */
+
+#include
+#include
+
+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/pintos-progos/examples/shell.c b/pintos-progos/examples/shell.c
new file mode 100644
index 0000000..93641b4
--- /dev/null
+++ b/pintos-progos/examples/shell.c
@@ -0,0 +1,104 @@
+#include
+#include
+#include
+#include
+
+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/pintos-progos/examples/test.c b/pintos-progos/examples/test.c
new file mode 100644
index 0000000..44dc307
--- /dev/null
+++ b/pintos-progos/examples/test.c
@@ -0,0 +1,101 @@
+/* test.c
+
+ Experiments with syscalls
+ argc < 2 Print Hello World
+ argv[1][0] == 'p' print argv[2]
+ == 'e' Exec Test
+ == 'f' File test
+ == 'F' File descriptor stress test
+ == 'h' Halt
+ == '0' Null-Pointer Access
+*/
+
+#include
+#include
+
+#define LARGE_BUF_SIZE 4150
+char large_buf[LARGE_BUF_SIZE];
+
+#define NUM_EXEC_CHILDS 7
+char *execs[NUM_EXEC_CHILDS] = { "test", "test p FOO", "test p BAR", "test f", "test 0", &large_buf[0], "test^" };
+
+#define MAX_FD 4097
+
+static void init_args(void);
+static void init_args()
+{
+ int i = 0;
+ char *t = "";
+ while(i < LARGE_BUF_SIZE-1) {
+ if(!*t) t = "test ";
+ large_buf[i++] = *t++;
+ }
+ large_buf[LARGE_BUF_SIZE-1]='\0';
+}
+
+int
+main (int argc, char** argv)
+{
+ if(argc < 2) {
+ printf("Hello World!\n");
+ exit(0);
+ }
+ init_args();
+ if(argv[1][0] == 'e') {
+ int r = 0;
+ int i;
+ int tid[NUM_EXEC_CHILDS];
+
+ for(i = 0; i < NUM_EXEC_CHILDS; i++) {
+ tid[i] = exec(execs[i]);
+ }
+ for(i = 0; i < NUM_EXEC_CHILDS; i++) {
+ if (tid[i] >= 0) {
+ r = wait(tid[i]);
+ printf("P child %d exited with exit code %d\n",i, r);
+ } else {
+ printf("P child %d failed to start\n", i);
+ }
+ }
+ } else if(argv[1][0] == 'f') {
+ char buf[10];
+ int r;
+ create ("test.txt", 10);
+ int handle = open ("test.txt");
+ if (handle < 2)
+ printf ("open(test.txt) returned %d", handle);
+ if ((r=write(handle,"987654321",10)) != 10) {
+ printf("write failed: %d not %d\n",r,10);
+ exit(1);
+ }
+ seek(handle,0);
+ if ((r=read(handle, buf, 10)) != 10) {
+ printf("read failed: %d not %d\n",r,10);
+ exit(1);
+ }
+ printf("test.txt: %s\n", buf);
+ } else if(argv[1][0] == 'F') {
+ int j,i;
+ create ("foo.txt", 10);
+ for (j = 0; j < 5; j++) {
+ for (i = 2; i <= MAX_FD; i++) {
+ if (open ("foo.txt") < 0) {
+ printf("Opening the %d's file failed\n",i-2);
+ break;
+ }
+ }
+ while(--i >= 2) {
+ close (i);
+ }
+ }
+ } else if(argv[1][0] == '0') {
+ printf("Null pointer value is: %d\n",*((int*)NULL));
+ } else if(argv[1][0] == 'h') {
+ halt();
+ } else if(argv[1][0] == 'p' && argc >= 3) {
+ printf("%s\n", argv[2]);
+ } else {
+ printf("ARGV[1] is %s\n", argv[1]);
+ }
+ return 0;
+}
diff --git a/pintos-progos/filesys/.gitignore b/pintos-progos/filesys/.gitignore
new file mode 100644
index 0000000..6d5357c
--- /dev/null
+++ b/pintos-progos/filesys/.gitignore
@@ -0,0 +1,3 @@
+build
+bochsrc.txt
+bochsout.txt
diff --git a/pintos-progos/filesys/Make.vars b/pintos-progos/filesys/Make.vars
new file mode 100644
index 0000000..b3aa005
--- /dev/null
+++ b/pintos-progos/filesys/Make.vars
@@ -0,0 +1,13 @@
+# -*- 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/pintos-progos/filesys/Makefile b/pintos-progos/filesys/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/pintos-progos/filesys/Makefile
@@ -0,0 +1 @@
+include ../Makefile.kernel
diff --git a/pintos-progos/filesys/directory.c b/pintos-progos/filesys/directory.c
new file mode 100644
index 0000000..030c1c9
--- /dev/null
+++ b/pintos-progos/filesys/directory.c
@@ -0,0 +1,236 @@
+#include "filesys/directory.h"
+#include
+#include
+#include
+#include "filesys/filesys.h"
+#include "filesys/inode.h"
+#include "threads/malloc.h"
+
+/* A directory. */
+struct dir
+ {
+ struct inode *inode; /* Backing store. */
+ off_t pos; /* Current position. */
+ };
+
+/* 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)
+{
+ return inode_create (sector, entry_cnt * sizeof (struct dir_entry));
+}
+
+/* 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)
+ {
+ dir->inode = inode;
+ dir->pos = 0;
+ 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;
+
+ 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;
+}
diff --git a/pintos-progos/filesys/directory.h b/pintos-progos/filesys/directory.h
new file mode 100644
index 0000000..930acf9
--- /dev/null
+++ b/pintos-progos/filesys/directory.h
@@ -0,0 +1,30 @@
+#ifndef FILESYS_DIRECTORY_H
+#define FILESYS_DIRECTORY_H
+
+#include
+#include
+#include "devices/block.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;
+
+/* 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]);
+
+#endif /* filesys/directory.h */
diff --git a/pintos-progos/filesys/file.c b/pintos-progos/filesys/file.c
new file mode 100644
index 0000000..d5fc10d
--- /dev/null
+++ b/pintos-progos/filesys/file.c
@@ -0,0 +1,168 @@
+#include "filesys/file.h"
+#include
+#include "filesys/inode.h"
+#include "threads/malloc.h"
+
+/* 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? */
+ };
+
+/* 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)
+{
+ return file_open (inode_reopen (file->inode));
+}
+
+/* Closes FILE. */
+void
+file_close (struct file *file)
+{
+ if (file != NULL)
+ {
+ file_allow_write (file);
+ 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;
+}
diff --git a/pintos-progos/filesys/file.h b/pintos-progos/filesys/file.h
new file mode 100644
index 0000000..a33c5af
--- /dev/null
+++ b/pintos-progos/filesys/file.h
@@ -0,0 +1,29 @@
+#ifndef FILESYS_FILE_H
+#define FILESYS_FILE_H
+
+#include "filesys/off_t.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 *);
+
+#endif /* filesys/file.h */
diff --git a/pintos-progos/filesys/filesys.c b/pintos-progos/filesys/filesys.c
new file mode 100644
index 0000000..7a53f5f
--- /dev/null
+++ b/pintos-progos/filesys/filesys.c
@@ -0,0 +1,103 @@
+#include "filesys/filesys.h"
+#include
+#include
+#include
+#include "filesys/file.h"
+#include "filesys/free-map.h"
+#include "filesys/inode.h"
+#include "filesys/directory.h"
+
+/* Partition that contains the file system. */
+struct block *fs_device;
+
+static void do_format (void);
+
+/* Initializes the file system module.
+ If FORMAT is true, reformats the file system. */
+void
+filesys_init (bool format)
+{
+ fs_device = block_get_role (BLOCK_FILESYS);
+ 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 ();
+}
+
+/* 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)
+{
+ 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;
+}
+
+/* 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)
+{
+ 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);
+}
+
+/* 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)
+{
+ struct dir *dir = dir_open_root ();
+ bool success = dir != NULL && dir_remove (dir, name);
+ dir_close (dir);
+
+ return success;
+}
+
+/* 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/pintos-progos/filesys/filesys.h b/pintos-progos/filesys/filesys.h
new file mode 100644
index 0000000..c1cda84
--- /dev/null
+++ b/pintos-progos/filesys/filesys.h
@@ -0,0 +1,20 @@
+#ifndef FILESYS_FILESYS_H
+#define FILESYS_FILESYS_H
+
+#include
+#include "filesys/off_t.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. */
+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);
+
+#endif /* filesys/filesys.h */
diff --git a/pintos-progos/filesys/free-map.c b/pintos-progos/filesys/free-map.c
new file mode 100644
index 0000000..29ea4df
--- /dev/null
+++ b/pintos-progos/filesys/free-map.c
@@ -0,0 +1,85 @@
+#include "filesys/free-map.h"
+#include
+#include
+#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/pintos-progos/filesys/free-map.h b/pintos-progos/filesys/free-map.h
new file mode 100644
index 0000000..316cd1c
--- /dev/null
+++ b/pintos-progos/filesys/free-map.h
@@ -0,0 +1,17 @@
+#ifndef FILESYS_FREE_MAP_H
+#define FILESYS_FREE_MAP_H
+
+#include
+#include
+#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/pintos-progos/filesys/fsutil.c b/pintos-progos/filesys/fsutil.c
new file mode 100644
index 0000000..447f291
--- /dev/null
+++ b/pintos-progos/filesys/fsutil.c
@@ -0,0 +1,222 @@
+#include "filesys/fsutil.h"
+#include
+#include
+#include
+#include
+#include
+#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);
+ 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, buffer + 1);
+
+ /* Finish up. */
+ file_close (src);
+ free (buffer);
+}
diff --git a/pintos-progos/filesys/fsutil.h b/pintos-progos/filesys/fsutil.h
new file mode 100644
index 0000000..cc73705
--- /dev/null
+++ b/pintos-progos/filesys/fsutil.h
@@ -0,0 +1,10 @@
+#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/pintos-progos/filesys/inode.c b/pintos-progos/filesys/inode.c
new file mode 100644
index 0000000..3463563
--- /dev/null
+++ b/pintos-progos/filesys/inode.c
@@ -0,0 +1,345 @@
+#include "filesys/inode.h"
+#include
+#include
+#include
+#include
+#include "filesys/filesys.h"
+#include "filesys/free-map.h"
+#include "threads/malloc.h"
+
+/* Identifies an inode. */
+#define INODE_MAGIC 0x494e4f44
+
+/* On-disk inode.
+ Must be exactly BLOCK_SECTOR_SIZE bytes long. */
+struct inode_disk
+ {
+ block_sector_t start; /* First data sector. */
+ off_t length; /* File size in bytes. */
+ unsigned magic; /* Magic number. */
+ uint32_t unused[125]; /* 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. */
+ };
+
+/* 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 (const struct inode *inode, off_t pos)
+{
+ ASSERT (inode != NULL);
+ if (pos < inode->data.length)
+ return inode->data.start + pos / BLOCK_SECTOR_SIZE;
+ else
+ return -1;
+}
+
+/* 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);
+}
+
+/* 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)
+ {
+ size_t sectors = bytes_to_sectors (length);
+ disk_inode->length = length;
+ disk_inode->magic = INODE_MAGIC;
+ if (free_map_allocate (sectors, &disk_inode->start))
+ {
+ block_write (fs_device, sector, disk_inode);
+ if (sectors > 0)
+ {
+ static char zeros[BLOCK_SECTOR_SIZE];
+ size_t i;
+
+ for (i = 0; i < sectors; i++)
+ block_write (fs_device, disk_inode->start + i, zeros);
+ }
+ 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;
+ block_read (fs_device, 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)
+ {
+ free_map_release (inode->sector, 1);
+ free_map_release (inode->data.start,
+ bytes_to_sectors (inode->data.length));
+ }
+
+ 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);
+ 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. */
+ block_read (fs_device, 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;
+ }
+ block_read (fs_device, 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);
+ 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. */
+ block_write (fs_device, 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)
+ block_read (fs_device, sector_idx, bounce);
+ else
+ memset (bounce, 0, BLOCK_SECTOR_SIZE);
+ memcpy (bounce + sector_ofs, buffer + bytes_written, chunk_size);
+ block_write (fs_device, 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;
+}
diff --git a/pintos-progos/filesys/inode.h b/pintos-progos/filesys/inode.h
new file mode 100644
index 0000000..cb42310
--- /dev/null
+++ b/pintos-progos/filesys/inode.h
@@ -0,0 +1,23 @@
+#ifndef FILESYS_INODE_H
+#define FILESYS_INODE_H
+
+#include
+#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 *);
+
+#endif /* filesys/inode.h */
diff --git a/pintos-progos/filesys/off_t.h b/pintos-progos/filesys/off_t.h
new file mode 100644
index 0000000..9caff4d
--- /dev/null
+++ b/pintos-progos/filesys/off_t.h
@@ -0,0 +1,15 @@
+#ifndef FILESYS_OFF_T_H
+#define FILESYS_OFF_T_H
+
+#include
+
+/* 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/pintos-progos/intro/Make.vars b/pintos-progos/intro/Make.vars
new file mode 100644
index 0000000..c612275
--- /dev/null
+++ b/pintos-progos/intro/Make.vars
@@ -0,0 +1,7 @@
+# -*- makefile -*-
+
+kernel.bin: DEFINES = -DUSERPROG -DFILESYS
+KERNEL_SUBDIRS = threads devices lib lib/kernel userprog filesys $(KERNEL_TESTS)
+KERNEL_TESTS = tests/intro/alarm-clock
+TEST_SUBDIRS = tests/intro/alarm-clock tests/intro/userprog-args
+GRADING_FILE = $(SRCDIR)/tests/intro/Grading
diff --git a/pintos-progos/intro/Makefile b/pintos-progos/intro/Makefile
new file mode 100644
index 0000000..34c10aa
--- /dev/null
+++ b/pintos-progos/intro/Makefile
@@ -0,0 +1 @@
+include ../Makefile.kernel
diff --git a/pintos-progos/lib/arithmetic.c b/pintos-progos/lib/arithmetic.c
new file mode 100644
index 0000000..bfc9b5a
--- /dev/null
+++ b/pintos-progos/lib/arithmetic.c
@@ -0,0 +1,189 @@
+#include
+
+/* 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/pintos-progos/lib/ctype.h b/pintos-progos/lib/ctype.h
new file mode 100644
index 0000000..9096aca
--- /dev/null
+++ b/pintos-progos/lib/ctype.h
@@ -0,0 +1,28 @@
+#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/pintos-progos/lib/debug.c b/pintos-progos/lib/debug.c
new file mode 100644
index 0000000..b4f8c2d
--- /dev/null
+++ b/pintos-progos/lib/debug.c
@@ -0,0 +1,32 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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/pintos-progos/lib/debug.h b/pintos-progos/lib/debug.h
new file mode 100644
index 0000000..888ab7b
--- /dev/null
+++ b/pintos-progos/lib/debug.h
@@ -0,0 +1,39 @@
+#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/pintos-progos/lib/inttypes.h b/pintos-progos/lib/inttypes.h
new file mode 100644
index 0000000..f703725
--- /dev/null
+++ b/pintos-progos/lib/inttypes.h
@@ -0,0 +1,48 @@
+#ifndef __LIB_INTTYPES_H
+#define __LIB_INTTYPES_H
+
+#include
+
+#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/pintos-progos/lib/kernel/bitmap.c b/pintos-progos/lib/kernel/bitmap.c
new file mode 100644
index 0000000..d14a98c
--- /dev/null
+++ b/pintos-progos/lib/kernel/bitmap.c
@@ -0,0 +1,371 @@
+#include "bitmap.h"
+#include
+#include
+#include
+#include
+#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/pintos-progos/lib/kernel/bitmap.h b/pintos-progos/lib/kernel/bitmap.h
new file mode 100644
index 0000000..a50593c
--- /dev/null
+++ b/pintos-progos/lib/kernel/bitmap.h
@@ -0,0 +1,51 @@
+#ifndef __LIB_KERNEL_BITMAP_H
+#define __LIB_KERNEL_BITMAP_H
+
+#include
+#include
+#include
+
+/* 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/pintos-progos/lib/kernel/console.c b/pintos-progos/lib/kernel/console.c
new file mode 100644
index 0000000..844b184
--- /dev/null
+++ b/pintos-progos/lib/kernel/console.c
@@ -0,0 +1,191 @@
+#include
+#include
+#include
+#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/pintos-progos/lib/kernel/console.h b/pintos-progos/lib/kernel/console.h
new file mode 100644
index 0000000..ab99249
--- /dev/null
+++ b/pintos-progos/lib/kernel/console.h
@@ -0,0 +1,8 @@
+#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/pintos-progos/lib/kernel/debug.c b/pintos-progos/lib/kernel/debug.c
new file mode 100644
index 0000000..b12f4f9
--- /dev/null
+++ b/pintos-progos/lib/kernel/debug.c
@@ -0,0 +1,123 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#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/pintos-progos/lib/kernel/hash.c b/pintos-progos/lib/kernel/hash.c
new file mode 100644
index 0000000..57eed45
--- /dev/null
+++ b/pintos-progos/lib/kernel/hash.c
@@ -0,0 +1,430 @@
+/* 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/pintos-progos/lib/kernel/hash.h b/pintos-progos/lib/kernel/hash.h
new file mode 100644
index 0000000..db9f674
--- /dev/null
+++ b/pintos-progos/lib/kernel/hash.h
@@ -0,0 +1,103 @@
+#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
+#include
+#include
+#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/pintos-progos/lib/kernel/list.c b/pintos-progos/lib/kernel/list.c
new file mode 100644
index 0000000..316d9ef
--- /dev/null
+++ b/pintos-progos/lib/kernel/list.c
@@ -0,0 +1,524 @@
+#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/pintos-progos/lib/kernel/list.h b/pintos-progos/lib/kernel/list.h
new file mode 100644
index 0000000..82efbb5
--- /dev/null
+++ b/pintos-progos/lib/kernel/list.h
@@ -0,0 +1,181 @@
+#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
+#include
+#include
+
+/* 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/pintos-progos/lib/kernel/stdio.h b/pintos-progos/lib/kernel/stdio.h
new file mode 100644
index 0000000..3e5bae9
--- /dev/null
+++ b/pintos-progos/lib/kernel/stdio.h
@@ -0,0 +1,6 @@
+#ifndef __LIB_KERNEL_STDIO_H
+#define __LIB_KERNEL_STDIO_H
+
+void putbuf (const char *, size_t);
+
+#endif /* lib/kernel/stdio.h */
diff --git a/pintos-progos/lib/limits.h b/pintos-progos/lib/limits.h
new file mode 100644
index 0000000..c957ec4
--- /dev/null
+++ b/pintos-progos/lib/limits.h
@@ -0,0 +1,34 @@
+#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/pintos-progos/lib/packed.h b/pintos-progos/lib/packed.h
new file mode 100644
index 0000000..9a9b6e2
--- /dev/null
+++ b/pintos-progos/lib/packed.h
@@ -0,0 +1,10 @@
+#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/pintos-progos/lib/random.c b/pintos-progos/lib/random.c
new file mode 100644
index 0000000..a4761b6
--- /dev/null
+++ b/pintos-progos/lib/random.c
@@ -0,0 +1,83 @@
+#include "random.h"
+#include
+#include
+#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;
+
+ 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/pintos-progos/lib/random.h b/pintos-progos/lib/random.h
new file mode 100644
index 0000000..0950ae2
--- /dev/null
+++ b/pintos-progos/lib/random.h
@@ -0,0 +1,10 @@
+#ifndef __LIB_RANDOM_H
+#define __LIB_RANDOM_H
+
+#include
+
+void random_init (unsigned seed);
+void random_bytes (void *, size_t);
+unsigned long random_ulong (void);
+
+#endif /* lib/random.h */
diff --git a/pintos-progos/lib/round.h b/pintos-progos/lib/round.h
new file mode 100644
index 0000000..3aa6642
--- /dev/null
+++ b/pintos-progos/lib/round.h
@@ -0,0 +1,18 @@
+#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/pintos-progos/lib/stdarg.h b/pintos-progos/lib/stdarg.h
new file mode 100644
index 0000000..32622b5
--- /dev/null
+++ b/pintos-progos/lib/stdarg.h
@@ -0,0 +1,14 @@
+#ifndef __LIB_STDARG_H
+#define __LIB_STDARG_H
+
+/* GCC has 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/pintos-progos/lib/stdbool.h b/pintos-progos/lib/stdbool.h
new file mode 100644
index 0000000..f173a91
--- /dev/null
+++ b/pintos-progos/lib/stdbool.h
@@ -0,0 +1,9 @@
+#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/pintos-progos/lib/stddef.h b/pintos-progos/lib/stddef.h
new file mode 100644
index 0000000..4e74fa6
--- /dev/null
+++ b/pintos-progos/lib/stddef.h
@@ -0,0 +1,12 @@
+#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/pintos-progos/lib/stdint.h b/pintos-progos/lib/stdint.h
new file mode 100644
index 0000000..ef5f214
--- /dev/null
+++ b/pintos-progos/lib/stdint.h
@@ -0,0 +1,51 @@
+#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/pintos-progos/lib/stdio.c b/pintos-progos/lib/stdio.c
new file mode 100644
index 0000000..8927c50
--- /dev/null
+++ b/pintos-progos/lib/stdio.c
@@ -0,0 +1,655 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* 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 ("<>", output, aux, *format);
+ break;
+
+ default:
+ __printf ("<>", 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/pintos-progos/lib/stdio.h b/pintos-progos/lib/stdio.h
new file mode 100644
index 0000000..2739c0a
--- /dev/null
+++ b/pintos-progos/lib/stdio.h
@@ -0,0 +1,40 @@
+#ifndef __LIB_STDIO_H
+#define __LIB_STDIO_H
+
+#include
+#include
+#include
+#include
+#include
+
+/* Include lib/user/stdio.h or lib/kernel/stdio.h, as
+ appropriate. */
+#include_next
+
+/* 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/pintos-progos/lib/stdlib.c b/pintos-progos/lib/stdlib.c
new file mode 100644
index 0000000..84c7f61
--- /dev/null
+++ b/pintos-progos/lib/stdlib.c
@@ -0,0 +1,208 @@
+#include
+#include
+#include
+#include
+#include
+
+/* 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/pintos-progos/lib/stdlib.h b/pintos-progos/lib/stdlib.h
new file mode 100644
index 0000000..d14afa3
--- /dev/null
+++ b/pintos-progos/lib/stdlib.h
@@ -0,0 +1,22 @@
+#ifndef __LIB_STDLIB_H
+#define __LIB_STDLIB_H
+
+#include
+
+/* 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/pintos-progos/lib/string.c b/pintos-progos/lib/string.c
new file mode 100644
index 0000000..d223c89
--- /dev/null
+++ b/pintos-progos/lib/string.c
@@ -0,0 +1,375 @@
+#include
+#include
+
+/* 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/pintos-progos/lib/string.h b/pintos-progos/lib/string.h
new file mode 100644
index 0000000..1fff82a
--- /dev/null
+++ b/pintos-progos/lib/string.h
@@ -0,0 +1,35 @@
+#ifndef __LIB_STRING_H
+#define __LIB_STRING_H
+
+#include
+
+/* 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/pintos-progos/lib/syscall-nr.h b/pintos-progos/lib/syscall-nr.h
new file mode 100644
index 0000000..21a7af9
--- /dev/null
+++ b/pintos-progos/lib/syscall-nr.h
@@ -0,0 +1,34 @@
+#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/pintos-progos/lib/user/console.c b/pintos-progos/lib/user/console.c
new file mode 100644
index 0000000..22bdc8c
--- /dev/null
+++ b/pintos-progos/lib/user/console.c
@@ -0,0 +1,94 @@
+#include
+#include
+#include
+#include
+
+/* 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/pintos-progos/lib/user/debug.c b/pintos-progos/lib/user/debug.c
new file mode 100644
index 0000000..f49b874
--- /dev/null
+++ b/pintos-progos/lib/user/debug.c
@@ -0,0 +1,25 @@
+#include
+#include
+#include
+#include
+#include
+
+/* 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/pintos-progos/lib/user/entry.c b/pintos-progos/lib/user/entry.c
new file mode 100644
index 0000000..a707c70
--- /dev/null
+++ b/pintos-progos/lib/user/entry.c
@@ -0,0 +1,10 @@
+#include
+
+int main (int, char *[]);
+void _start (int argc, char *argv[]);
+
+void
+_start (int argc, char *argv[])
+{
+ exit (main (argc, argv));
+}
diff --git a/pintos-progos/lib/user/stdio.h b/pintos-progos/lib/user/stdio.h
new file mode 100644
index 0000000..b9f3cc6
--- /dev/null
+++ b/pintos-progos/lib/user/stdio.h
@@ -0,0 +1,7 @@
+#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/pintos-progos/lib/user/syscall.c b/pintos-progos/lib/user/syscall.c
new file mode 100644
index 0000000..0a467f8
--- /dev/null
+++ b/pintos-progos/lib/user/syscall.c
@@ -0,0 +1,184 @@
+#include
+#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 *cmd_line)
+{
+ return (pid_t) syscall1 (SYS_EXEC, cmd_line);
+}
+
+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/pintos-progos/lib/user/syscall.h b/pintos-progos/lib/user/syscall.h
new file mode 100644
index 0000000..a1bcf0b
--- /dev/null
+++ b/pintos-progos/lib/user/syscall.h
@@ -0,0 +1,48 @@
+#ifndef __LIB_USER_SYSCALL_H
+#define __LIB_USER_SYSCALL_H
+
+#include
+#include
+
+/* 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 *cmd_line);
+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/pintos-progos/lib/user/user.lds b/pintos-progos/lib/user/user.lds
new file mode 100644
index 0000000..cc6d6c0
--- /dev/null
+++ b/pintos-progos/lib/user/user.lds
@@ -0,0 +1,57 @@
+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) }
+
+ /* 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/pintos-progos/lib/ustar.c b/pintos-progos/lib/ustar.c
new file mode 100644
index 0000000..49af69a
--- /dev/null
+++ b/pintos-progos/lib/ustar.c
@@ -0,0 +1,228 @@
+#include
+#include
+#include
+#include
+#include
+
+/* 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/pintos-progos/lib/ustar.h b/pintos-progos/lib/ustar.h
new file mode 100644
index 0000000..43a5513
--- /dev/null
+++ b/pintos-progos/lib/ustar.h
@@ -0,0 +1,29 @@
+#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
+
+/* 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/pintos-progos/misc/0001-bochs-2.3.7-jitter.patch b/pintos-progos/misc/0001-bochs-2.3.7-jitter.patch
new file mode 100644
index 0000000..44190e3
--- /dev/null
+++ b/pintos-progos/misc/0001-bochs-2.3.7-jitter.patch
@@ -0,0 +1,78 @@
+From 5e6cfa27ba6de331ecc142e7f65b4d1c2112b4e2 Mon Sep 17 00:00:00 2001
+From: Alex Busenius
+Date: Mon, 27 Apr 2009 15:33:37 +0200
+Subject: bochs-2.3.7 jitter
+
+---
+ bochs.h | 2 ++
+ iodev/pit82c54.cc | 9 ++++++++-
+ main.cc | 8 ++++++++
+ 3 files changed, 18 insertions(+), 1 deletions(-)
+
+diff --git a/bochs.h b/bochs.h
+index 2a643cd..75bcd96 100644
+--- a/bochs.h
++++ b/bochs.h
+@@ -630,4 +630,6 @@ void bx_center_print(FILE *file, const char *line, unsigned maxwidth);
+
+ #endif
+
++extern int jitter;
++
+ #endif /* BX_BOCHS_H */
+diff --git a/iodev/pit82c54.cc b/iodev/pit82c54.cc
+index 0d65768..31ac041 100644
+--- a/iodev/pit82c54.cc
++++ b/iodev/pit82c54.cc
+@@ -28,6 +28,7 @@
+
+ #include "iodev.h"
+ #include "pit82c54.h"
++#include
+ #define LOG_THIS this->
+
+
+@@ -399,7 +400,13 @@ pit_82C54::clock(Bit8u cnum)
+ 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 --git a/main.cc b/main.cc
+index ebdf258..09cf661 100644
+--- a/main.cc
++++ b/main.cc
+@@ -112,6 +112,7 @@ BOCHSAPI BX_MEM_C bx_mem;
+ #endif
+
+ char *bochsrc_filename = NULL;
++int jitter = 0;
+
+ void bx_print_header ()
+ {
+@@ -541,6 +542,13 @@ int bx_init_main(int argc, char *argv[])
+ else if (!strcmp("-q", argv[arg])) {
+ SIM->get_param_enum(BXPN_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];
+--
+1.6.2.3
+
diff --git a/pintos-progos/misc/0002-bochs-2.3.7-triple-fault.patch b/pintos-progos/misc/0002-bochs-2.3.7-triple-fault.patch
new file mode 100644
index 0000000..c8698bd
--- /dev/null
+++ b/pintos-progos/misc/0002-bochs-2.3.7-triple-fault.patch
@@ -0,0 +1,87 @@
+From 356b7e781c815c70c992d58360caa42f1776d06b Mon Sep 17 00:00:00 2001
+From: Alex Busenius
+Date: Mon, 27 Apr 2009 17:09:27 +0200
+Subject: bochs-2.3.7 triple fault
+
+---
+ cpu/cpu.h | 4 ++++
+ cpu/exception.cc | 7 +++++++
+ gdbstub.cc | 11 ++++++++---
+ 3 files changed, 19 insertions(+), 3 deletions(-)
+
+diff --git a/cpu/cpu.h b/cpu/cpu.h
+index 7c7b11b..c47133a 100644
+--- a/cpu/cpu.h
++++ b/cpu/cpu.h
+@@ -903,6 +903,10 @@ public: // for now...
+ #endif
+ Bit8u trace;
+
++#if BX_GDBSTUB
++ Bit8u ispanic;
++#endif
++
+ // for paging
+ struct {
+ bx_TLB_entry entry[BX_TLB_SIZE] BX_CPP_AlignN(16);
+diff --git a/cpu/exception.cc b/cpu/exception.cc
+index c3e3777..fb3abfc 100644
+--- a/cpu/exception.cc
++++ b/cpu/exception.cc
+@@ -856,6 +856,13 @@ void BX_CPU_C::exception(unsigned vector, Bit16u error_code, bx_bool trap)
+ // trap into debugger (similar as done when PANIC occured)
+ bx_debug_break();
+ #endif
++#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 (SIM->get_param_bool(BXPN_RESET_ON_TRIPLE_FAULT)->get()) {
+ BX_ERROR(("exception(): 3rd (%d) exception with no resolution, shutdown status is %02xh, resetting", vector, DEV_cmos_get_reg(0x0f)));
+ bx_pc_system.Reset(BX_RESET_SOFTWARE);
+diff --git a/gdbstub.cc b/gdbstub.cc
+index f58f866..bc5ed61 100644
+--- a/gdbstub.cc
++++ b/gdbstub.cc
+@@ -471,7 +471,12 @@ static void debug_loop(void)
+ }
+
+ stub_trace_flag = 0;
++ bx_cpu.ispanic = 0;
+ bx_cpu.cpu_loop(0);
++ if (bx_cpu.ispanic)
++ {
++ last_stop_reason = GDBSTUB_EXECUTION_BREAKPOINT;
++ }
+
+ DEV_vga_refresh();
+
+@@ -502,19 +507,19 @@ static void debug_loop(void)
+
+ BX_INFO(("stepping"));
+ stub_trace_flag = 1;
++ bx_cpu.ispanic = 0;
+ bx_cpu.cpu_loop(0);
+ 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;
+--
+1.6.2.3
+
diff --git a/pintos-progos/misc/0003-bochs-2.3.7-page-fault-segv.patch b/pintos-progos/misc/0003-bochs-2.3.7-page-fault-segv.patch
new file mode 100644
index 0000000..8b6e090
--- /dev/null
+++ b/pintos-progos/misc/0003-bochs-2.3.7-page-fault-segv.patch
@@ -0,0 +1,93 @@
+From 314833401978558db06bbb4f4f76e4dc7b603744 Mon Sep 17 00:00:00 2001
+From: Alex Busenius
+Date: Mon, 27 Apr 2009 16:33:54 +0200
+Subject: bochs-2.3.7 page fault segv
+
+---
+ bochs.h | 1 +
+ cpu/exception.cc | 4 ++++
+ gdbstub.cc | 17 ++++++++++++++++-
+ 3 files changed, 21 insertions(+), 1 deletions(-)
+
+diff --git a/bochs.h b/bochs.h
+index 75bcd96..657c7b8 100644
+--- a/bochs.h
++++ b/bochs.h
+@@ -433,6 +433,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
+diff --git a/cpu/exception.cc b/cpu/exception.cc
+index fb3abfc..8dac5ca 100644
+--- a/cpu/exception.cc
++++ b/cpu/exception.cc
+@@ -1046,6 +1046,10 @@ void BX_CPU_C::exception(unsigned vector, Bit16u error_code, bx_bool trap)
+
+ BX_CPU_THIS_PTR errorno++;
+
++#if BX_GDBSTUB
++ bx_gdbstub_exception(vector);
++#endif
++
+ if (real_mode()) {
+ // not INT, no error code pushed
+ BX_CPU_THIS_PTR interrupt(vector, 0, 0, 0);
+diff --git a/gdbstub.cc b/gdbstub.cc
+index bc5ed61..ad59373 100644
+--- a/gdbstub.cc
++++ b/gdbstub.cc
+@@ -47,6 +47,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;
+@@ -323,6 +324,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;
+@@ -493,6 +500,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);
+@@ -517,10 +528,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;
+ }
+--
+1.6.2.3
+
diff --git a/pintos-progos/misc/bochs-2.3.7-build.sh b/pintos-progos/misc/bochs-2.3.7-build.sh
new file mode 100755
index 0000000..57e35d1
--- /dev/null
+++ b/pintos-progos/misc/bochs-2.3.7-build.sh
@@ -0,0 +1,42 @@
+#! /bin/sh -e
+
+if test -z "$SRCDIR" || test -z "$PINTOSDIR" || test -z "$DSTDIR"; then
+ echo "usage: env SRCDIR= PINTOSDIR= DSTDIR= sh $0"
+ echo " where contains bochs-2.3.7.tar.gz"
+ echo " and is the root of the pintos source tree"
+ echo " and is the installation prefix (e.g. /usr/local)"
+ exit 1
+fi
+
+cd /tmp
+mkdir bochs-pintos-$$
+cd bochs-pintos-$$
+mkdir bochs-2.3.7
+tar xzf $SRCDIR/bochs-2.3.7.tar.gz
+cd bochs-2.3.7
+cat $PINTOSDIR/src/misc/0001-bochs-2.3.7-jitter.patch | patch -p1
+cat $PINTOSDIR/src/misc/0002-bochs-2.3.7-triple-fault.patch | patch -p1
+cat $PINTOSDIR/src/misc/0003-bochs-2.3.7-page-fault-segv.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.3.7-gcc43.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.3.7-typos.patch | patch -p1
+cat $PINTOSDIR/src/misc/bochs-2.3.7-linux3x.patch | patch -p1
+autoconf
+
+CFGOPTIONAL="--enable-large-pages --enable-mmx --enable-usb --enable-pci --enable-pcidev --enable-acpi --enable-global-pages --enable-show-ips"
+CFGOPTIMIZE="--enable-all-optimizations --enable-guest2host-tlb --enable-repeat-speedups --enable-trace-cache --enable-icache --enable-fast-function-calls --enable-idle-hack "
+CFGOPTS="--prefix=$DSTDIR --enable-ignore-bad-msr --enable-disasm --enable-logging --enable-fpu --enable-alignment-check --enable-plugins --enable-cpu-level=6 --enable-readline --without-sdl --without-svga --without-wx --with-x --with-x11 --with-term --with-nogui $CFGOPTIONAL"
+mkdir plain &&
+ cd plain &&
+ ../configure $CFGOPTS --enable-gdb-stub &&
+# make -j3 &&
+ make && echo "done building plain" &&
+ sudo make install &&
+ cd .. &&
+mkdir with-dbg &&
+ cd with-dbg &&
+ ../configure --enable-debugger $CFGOPTS &&
+ # make -j3 &&
+ make && echo "done building with-dbg" &&
+ sudo cp -v bochs $DSTDIR/bin/bochs-dbg &&
+ cd .. &&
+ echo "SUCCESS"
diff --git a/pintos-progos/misc/bochs-2.3.7-gcc43.patch b/pintos-progos/misc/bochs-2.3.7-gcc43.patch
new file mode 100644
index 0000000..4646edf
--- /dev/null
+++ b/pintos-progos/misc/bochs-2.3.7-gcc43.patch
@@ -0,0 +1,12 @@
+--- bochs-2.3.7.orig/bx_debug/symbols.cc 2008/03/30 14:32:14 1.11
++++ bochs-2.3.7/bx_debug/symbols.cc 2008/06/16 17:09:52 1.12
+@@ -95,6 +95,9 @@
+ #endif
+
+ using namespace std;
++#ifdef __GNUC__
++using namespace __gnu_cxx;
++#endif
+
+ struct symbol_entry_t
+ {
diff --git a/pintos-progos/misc/bochs-2.3.7-linux3x.patch b/pintos-progos/misc/bochs-2.3.7-linux3x.patch
new file mode 100644
index 0000000..1c84060
--- /dev/null
+++ b/pintos-progos/misc/bochs-2.3.7-linux3x.patch
@@ -0,0 +1,11 @@
+--- a/configure.in 2012-01-03 11:12:22.104612131 +0100
++++ b/configure.in 2012-01-03 11:13:05.507941106 +0100
+@@ -715,7 +715,7 @@ AC_ARG_ENABLE(pcidev,
+ PCIDEV_MODULE_MAKE_ALL="all-kernel24"
+ KERNEL_MODULE_SUFFIX="o"
+ ;;
+- 2.6*)
++ 2.6*|3*)
+ PCIDEV_MODULE_MAKE_ALL="all-kernel26"
+ KERNEL_MODULE_SUFFIX="ko"
+ ;;
diff --git a/pintos-progos/misc/bochs-2.3.7-typos.patch b/pintos-progos/misc/bochs-2.3.7-typos.patch
new file mode 100644
index 0000000..c9fb168
--- /dev/null
+++ b/pintos-progos/misc/bochs-2.3.7-typos.patch
@@ -0,0 +1,24 @@
+diff -NaurwB bochs-2.3.7.orig/cpu/ia_opcodes.h bochs-2.3.7/cpu/ia_opcodes.h
+--- bochs-2.3.7.orig/cpu/ia_opcodes.h 2008-05-30 22:35:08.000000000 +0200
++++ bochs-2.3.7/cpu/ia_opcodes.h 2008-06-04 14:56:46.000000000 +0200
+@@ -891,7 +891,7 @@
+ bx_define_opcode(BX_IA_PF2ID_PqQq, BX_CPU_C::PF2ID_PqQq)
+ bx_define_opcode(BX_IA_PF2IW_PqQq, BX_CPU_C::PF2IW_PqQq)
+ bx_define_opcode(BX_IA_PFACC_PqQq, BX_CPU_C::PFACC_PqQq)
+-bx_define_opcode(BX_IA_PFADD_PqQq, BX_CPU_C::BX_PFADD_PqQq)
++bx_define_opcode(BX_IA_PFADD_PqQq, BX_CPU_C::PFADD_PqQq)
+ bx_define_opcode(BX_IA_PFCMPEQ_PqQq, BX_CPU_C::PFCMPEQ_PqQq)
+ bx_define_opcode(BX_IA_PFCMPGE_PqQq, BX_CPU_C::PFCMPGE_PqQq)
+ bx_define_opcode(BX_IA_PFCMPGT_PqQq, BX_CPU_C::PFCMPGT_PqQq)
+diff -NaurwB bochs-2.3.7.orig/iodev/iodebug.h bochs-2.3.7/iodev/iodebug.h
+--- bochs-2.3.7.orig/iodev/iodebug.h 2008-05-01 22:46:58.000000000 +0200
++++ bochs-2.3.7/iodev/iodebug.h 2008-06-04 14:45:50.000000000 +0200
+@@ -18,7 +18,7 @@
+ virtual void init(void);
+ virtual void reset (unsigned type) {}
+ static void mem_write(BX_CPU_C *cpu, bx_phy_address addr, unsigned len, void *data);
+- static void mem_read(BX_CPU_C *cpu, bx_phy_addressu addr, unsigned len, void *data);
++ static void mem_read(BX_CPU_C *cpu, bx_phy_address addr, unsigned len, void *data);
+
+ private:
+ static Bit32u read_handler(void *this_ptr, Bit32u address, unsigned io_len);
diff --git a/pintos-progos/misc/gcc-3.3.6-cross-howto b/pintos-progos/misc/gcc-3.3.6-cross-howto
new file mode 100644
index 0000000..ad25173
--- /dev/null
+++ b/pintos-progos/misc/gcc-3.3.6-cross-howto
@@ -0,0 +1,39 @@
+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/pintos-progos/misc/gdb-macros b/pintos-progos/misc/gdb-macros
new file mode 100644
index 0000000..a0b68c3
--- /dev/null
+++ b/pintos-progos/misc/gdb-macros
@@ -0,0 +1,140 @@
+#
+# 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 , 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/pintos-progos/notes/1.txt b/pintos-progos/notes/1.txt
new file mode 100644
index 0000000..9d478f7
--- /dev/null
+++ b/pintos-progos/notes/1.txt
@@ -0,0 +1,81 @@
+Getting Started with PINTOS
+===========================
+
+Building Project 1
+------------------
+
+pintos $ cd src/threads
+threads $ make
+
+
+Building Bochs
+--------------
+You should have a patched bochs install available.
+
+See
+
+ http://courses.mpi-sws.org/os-ss11/assignments/pintos/pintos_12.html#SEC160
+
+There is a build script src/misc/bochs-2.3.7-build.sh in the pintos fork from Saarland,
+which (after small modifications) works on a modern Ubuntu x86.
+
+For Ubuntu 11 with a Linux 3.0 kernel, you need to:
+
+ * Regenerate the configure script (autoconf configure.in)
+ * Patch the test for Linux 2.4 or 2.6
+
+After building, copy bochs and bochs-gdb to the pintos/src/utils directory
+
+Running
+-------
+
+ # [pintos/src]
+ PATH=`pwd`/utils:$PATH
+
+ cd threads/build
+ # [pintos/src/threads/build]
+ pintos run alarm-multiple > logfile
+
+
+### Reproducability
+
+This command line flags to pintos influence reproducability.
+Remember: you need the patched bochs build.
+
+ -j seed ... Reproducible behavior
+ -r ... Real-Time behavior
+
+Running with qemu
+-----------------
+
+ # [pintos/src]
+ vim utils/pintos # comment line with -no-kqemu flag
+
+ cd threads/build
+ # [pintos/src/threads/build]
+ pintos --qemu -- run alarm-multiple
+
+Debugging
+---------
+
+pintos $ vim utils/pintos-gdb
+
+ GDBMACROS=${PINTOS_SRC}/misc/gdb-macros
+
+[pts/0 build] $ pintos --gdb -- run alarm-multiple
+[pts/1 build] $ pintos-gdb kernel.o
+(gdb) debugpintos
+
+Testing
+-------
+
+* Running all tests
+
+ build $ make check
+
+* Running a single test
+
+ build $ rm tests/threads/alarm-multiple.result
+ build $ make tests/threads/alarm-multiple.result
+
+
diff --git a/pintos-progos/notes/2.txt b/pintos-progos/notes/2.txt
new file mode 100644
index 0000000..c81b980
--- /dev/null
+++ b/pintos-progos/notes/2.txt
@@ -0,0 +1,164 @@
+Projekt 1 - Threads
+===================
+
+alarm clock
+-----------
+The simplest strategy is to maintain a wait list for
+all threads blocked for sleep.
+
+ * In 'timer_interrupt', check for threads which can be
+ unblocked from sleeping
+ * In 'sleep', set sleep timeout in thread, block the
+ thread and put it on the sleep list
+
+Notes:
+
+ * There are three places where a thread is added to the
+ ready list:
+ - thread_init
+ - thread_yield
+ - thread_unblock
+ * Iterate list with removal:
+ for (e = list_begin (&list); e != list_end (&list); )
+ if(...)
+ e = list_remove(e)->prev;
+ /* Unblock must be called AFTER removing, as thread.elem is reused */
+ else
+ e = list_next(e);
+
+Stats:
+
+ pintos/src/devices/timer.c | 40 ++++++++++++++++++++++++++++++++++++++--
+ pintos/src/threads/thread.h | 3 +++
+ 2 files changed, 41 insertions(+), 2 deletions(-)
+
+ Design & Implementation time: 4 hours
+
+Priority Scheduler
+------------------
+
+A simple implementation of the priority scheduler (64 priority levels, round robin within
+one priority group).
+
+ * If a new task arrives with a higher priority, switch to this group
+ * If the currently active group is empty, search for the group with the next highest priority
+
+Notes:
+
+ * thread_{init,unblock,yield} now call thread_ready, which updates the lowest ready priority
+ * The thread_unblock operation does not yield a new thread immediately. Therefore, we need to check
+ later whether we need to switch to a higher priority thread (via thread_yield).
+ As thread_unblock is called with interrupts off, it seemed best to perform
+ this check when interrupts are enabled. This is only necessary if a higher priority task
+ is ready.
+ * First attempt passed alarm-priority, but failed to pass the priority-preempt test.
+ But the debugging facilities are fantastic, so it was easy to spot the problem
+ * Wolfgang suggested to yield a software interrupt when unblocking instead of modifying
+ interrupt_enable.
+
+Stats:
+
+ pintos/src/threads/interrupt.c | 3 +-
+ pintos/src/threads/thread.c | 60 ++++++++++++++++++++++++++++++++--------
+ pintos/src/threads/thread.h | 1 +
+ 3 files changed, 51 insertions(+), 13 deletions(-)
+
+ Design and implementation time: 3 hours
+
+Priority Locks
+--------------
+
+We also need to select higher priority task first from locks, semaphores and condition variables.
+This easiest implementation searches for the thread with the highest priority in the wait queue.
+
+Notes:
+
+ * It is sufficient to implement the priority based selection twice, for sema_up and
+ cond_signal. cond_signal is a little bit harder, as we need to store the priority
+ (or the waiting thread) in the semaphore_elem type
+ * There are some handy list utility functions; in this case, list_max does a fine job
+ for both sema_up and cond_signal
+ * It is difficult to implement this in an efficient (sublinear) way, because priority donation
+ may boost a thread at any time!
+
+Stats:
+
+ pintos/src/threads/synch.c | 40 ++++++++++++++++++++++++++++++++++------
+ 1 files changed, 34 insertions(+), 6 deletions(-)
+
+ Design and Implementation time: 1 hour
+
+Priority Donation
+-----------------
+If a thread aquires a lock, the lock holder needs to be boosted to the donated priority.
+We need to deal with nesting and chaining:
+
+ * Lock/Thread correspondence: Each lock is associated with at most one thread that holds it.
+ Therefore, donated priority can be associated with a lock.
+ * If a thread t wants to obtain a lock L, and a thread with a lower priority holds it,
+ the thread holding the lock is boosted to the priority of the requesting thread
+ * Chaining: If the boosted thread is also blocked on a lock, than we also need to donate
+ the priority to that lock, in a transitive way.
+ * Nesting: If a thread may hold more than one lock, we need to keep track of the donation
+ to each lock. When a lock is released or the static priority changes, the highest priority
+ donated to other locks is assigned to the thread.
+
+With this information, the following rules seem suitable (without proof of correctness):
+
+ * If thread T tries to aquire lock L
+
+ ==> if(L.owner)
+ T.locked_on = L
+ donate_priority (L, T.priority)
+ end
+ donate_priority (L ,p) :=
+ L.priority v= p
+ L.holder.priority v= p
+ donate_priority( L.holder.locked_on, p)
+ end
+
+ * If a thread T aquires a lock L
+
+ ==> L.holder = T
+ T.locks += L
+ T.lock_on = none
+
+
+ * If a thread T releases a lock L
+
+ ==> L.holder = none
+ T.locks -= L
+ T.priority = max (T.locks.priority, static_priority)
+
+To implement this, each thread needs to maintain a list of locks, a static priority,
+and a reference to the lock blocking it.
+
+Notes:
+
+ * Difficult to design, really a challenge.
+ Literature (short paper) could be helpful.
+ Maybe it would be interesting to ask for correctness proof sketches?
+
+ * First design was wrong, because I assumed locks are aquired in FIFO fashion
+ * First implementation failed to pass donate tests, due to a typo. Debugging
+ facilities are fantastic, no problem to spot this one.
+ * Next try, priority-donate-lower failed. Looking at the source code revealed that
+ we need to recompute the priority at the end of thread_set_priority.
+ * Next try, priority-donate-chain failed. Chaining is tricky to get right;
+ in my implementation, chained donations were lost after one lock was released.
+
+ * It would be an interesting option to ask for new test cases from the students
+ * I think it would also be a cool task to write a test for a RMS scheduling
+ scenario with blocking.
+
+Stats:
+
+ pintos/src/threads/synch.c | 15 ++++++++++++
+ pintos/src/threads/synch.h | 6 +++-
+ pintos/src/threads/thread.c | 53 +++++++++++++++++++++++++++++++++++++++++-
+ pintos/src/threads/thread.h | 9 ++++++-
+ pintos/src/utils/pintos | 2 +-
+
+ Design: 5h
+ Implementation: 3h
+
diff --git a/pintos-progos/notes/3.txt b/pintos-progos/notes/3.txt
new file mode 100644
index 0000000..bc64f88
--- /dev/null
+++ b/pintos-progos/notes/3.txt
@@ -0,0 +1,241 @@
+Project 2
+=========
+
+Working with Disks
+------------------
+
+Assumes you ran make in src/userprog and src/examples.
+
+ * Create a 2 MB hard disk for pintos
+
+ # [src/userprog/build]
+ pintos-mkdisk filesys.dsk --filesys-size=2
+
+ * Format Disk
+
+ # -f ... format virtual disk
+ pintos -f -q
+
+ * Copy file to filesystem
+
+ # -p FILE ... file to put on virtual disk
+ # -a FILE ... newname on virtual disk
+ pintos -p ../../examples/echo -a echo -- -q
+
+ * Execute echo, and get file 'echo' from the virtual disk
+
+ pintos -g echo -- -q run 'echo x'
+
+Putting all together, we can run an minimal example like that:
+
+ # [src/userprog/build]
+ pintos --filesys-size=2 -p ../../examples/halt -a halt -- -f -q run 'halt'
+
+Getting Started
+---------------
+
+ * Fix the problem with the .note.gnu.build-id segment
+
+ * Change the stack setup in process.c#setup_stack() to
+
+ *esp = PHYS_BASE - 12;
+
+ * Change process_wait() to an infinite loop
+
+This should be enough to see 'system call!' when executing
+the 'halt' example.
+
+Next, we need to implement user memory access and the
+the system call dispatcher, as well as the basic
+system calls halt, exit and write.
+
+A simple implementation of user memory access first checks
+whether the address is in user space, and the calls load_page.
+
+For an initial system call dispatcher, we convert the stack pointer
+saved by the processor during the interrupt to kernel space, and
+then dispatch to halt, exit and write. For now, exit just terminates
+the process, and write uses printf, ignoring the fd argument.
+The return value is stored into %eax.
+
+Notes:
+ * halt(): There is no function shutdown() in init.h, only
+ shutdown_poweroff in shutdown.h
+
+ * When accessing data from user space in kernel space, we need to be
+ sure that the entire address ranged accessed is in user space.
+ Note that pointers are not necessarily aligned, and thus might
+ involve two user pages.
+ Furthermore, buffers need to be copied to kernel space;
+ otherwise, concurrent user space operations could corrupt the kernel.
+ Linux allows at most one kernel page for such buffers; we follow
+ the same route.
+
+ * Debugging: the function hex_dump() is useful; no need to
+ reimplement it.
+
+ * Something went wrong with the write system call, and this
+ is rather tricky to debug.
+ I invoked the system call directly, using inline
+ assembly; this worked fine?
+ Then I tried to debug the user space program; to this
+ end, lookup the code address you are interested in,
+ and use gdb together with objdump for debugging:
+
+ Debugging 'write(1,"USA\n",4)'
+
+ break *0x0804820e # break at
+ cont # pushl 0xc(%esp)
+ info registers # esp = 0xbfffffbc
+ x/1w (0xbfffffbc+0xc) # ==> 4 (length)
+ stepi # pushl 0x8(%esp)
+ info registers # esp = 0x......b8
+ x/1w 0xbfffffb8 # ==> 4 (TOS)
+ x/1w (0xbfffffb8+8) # ==> 1 (wrong) !!!
+
+ Apparently, the inline assembler in pintos does not use
+ the right constraints.
+
+Stat:
+ pintos/src/lib/user/syscall.c | 6 +-
+ pintos/src/userprog/process.c | 5 ++-
+ pintos/src/userprog/syscall.c | 92 ++++++++++++++++++++++++++++++++++++++--
+
+ Reading and Implementation Time: 6 hours
+ Debugging Syscalls: 5 hours
+
+
+Argument Passing
+----------------
+First, we tokenize the command using strtok_r, and then setup
+the stack.
+
+Notes:
+ * As noted in the doc, just using strtok_r seems fine.
+ However, as strtok_r modifies the string even if only
+ the first token is needed, some copying is involved
+ if it is used to obtain the filename.
+ * Due to the detailed description in the documentation,
+ setting up the stack is mostly implementation work.
+ * One of the trickier implementation aspects is that we
+ modify the stack in kernel space, but need to convert
+ pointers to user space before pushing them on the stack.
+ * Debugging: Optimizations were really troublesome debugging
+ this task; making setup_stack non-static at least helped
+ to set a suitable breakpoint. In the end, printf was the
+ better debugging aid for this task.
+
+Stat:
+ pintos/src/userprog/process.c | 116 +++++++++++++++++++++++++++++++++--------
+
+ Design and Implementation Time: 4 hours
+
+
+Process Management: exec, wait and exit
+---------------------------------------
+The wait system call requires that all children
+of a process are known, that the exit code of
+a process is stored until collected by the parent,
+and that the parent can block until the child
+process terminates.
+
+One difficult aspect in the design is that kernel
+threads are not processes, and that child threads
+may exit after their parent. It is important to
+note that threads do not need to wait for their
+children, but that we need to keep the exit status
+until the parent exits.
+
+In the original design, a thread is cleaned up when
+in the scheduler right after it died. In our design
+we delay the cleanup if the parent thread is still alive.
+
+Another issue is that thread_create needs to block
+until the load process of the child thread has finished.
+
+Notes:
+ * I wanted to use the same semaphore for startup and wait.
+ This works, but we need one additional variable (or bit)
+ to distinguish failure at load time from failure at
+ runtime.
+ * Ugly 1: thread_create only gives back a tid,
+ so it is not possible to directly access the semaphore
+ in process_execute. Therefore we need to iterate over the
+ child list (which is not that bad, because if loading failed,
+ the child needs to be removed from the list anyway).
+ * Ugly 2: We up the semaphore used to synchronize
+ with process_execute and process_wait in thread.c, for
+ all threads.
+ * As also noted by rene, it is important to identifiy memory leaks,
+ as early as possible. To this end, first add debug messages to
+ page_alloc/page_free, and then run test programs to identify leaking
+ pages. Then debug, add conditional breakpoints to stop when a leaking
+ page is allocated, and inspect the stacktrace to find the culprit.
+
+Stats:
+ pintos/src/threads/thread.c | 31 +++++++++++++++++---
+ pintos/src/threads/thread.h | 8 +++++
+ pintos/src/userprog/process.c | 60 ++++++++++++++++++++++++++++++++--------
+ pintos/src/userprog/syscall.c | 19 +++++++++---
+
+ Design and Implementation Time: 7 hours
+
+File I/O System Calls
+---------------------
+For file I/O we need to implement synchronization (filesys is not thread safe).
+The documentation states that it is not recommended to modify the code in
+the filesys directory for now. A very simple solution is to use one lock for all filesystem operations, including process.c#load.
+Furthermore, we need to deny writes to a a file currently running as a user
+space process.
+
+Notes:
+ * init_thread() must not aquire locks, and thus not allocate pages.
+ Otherwise, the initialization of the init thread fails.
+ * The {lg,sm}-full tests failed in the initial implementation;
+ apparently, the read/write system calls should always read/write the
+ full amount of bytes specified to pass this tests. This was not
+ clear from the assignment.
+ * It is not obvious that file_close calls file_allow_write. But an
+ executable should not be writeable during its execution. Therefore,
+ one needs to make sure that it stays write protected after loading
+ has finished. I solve this by keeping the executable open during
+ execution.
+ * The multi-oom test failed again; debugging revealed that I forgot
+ to close all files at process_exit.
+
+Stats:
+
+ pintos/src/threads/thread.c | 1 +
+ pintos/src/threads/thread.h | 6 +-
+ pintos/src/userprog/process.c | 53 ++++-
+ pintos/src/userprog/process.h | 2 +
+ pintos/src/userprog/syscall.c | 435 +++++++++++++++++++++++++++++++-----
+ pintos/src/userprog/syscall.h | 1 +
+ 6 files changed, 381 insertions(+), 117 deletions(-)
+ Design and Implementation Time: 8 hours
+
+Improved User Memory Access
+---------------------------
+Looking at Project 3, it is a much better idea to not check whether a user
+space page is valid, but just let the page fault handler do the job.
+I decided to exit the process in the page fault handler if the address
+is in user space. One needs to take care of temporary memory allocated
+by the syscall handler, to avoid memory leaks. To this end, temporary kernel
+pages allocated in the handler are recorded and either freed at the end
+of the syscall or the end of the process.
+
+Notes:
+ * When using this approach, it is vital to copy user buffers
+ before reading or writing. With virtual memory, a page fault may
+ require to access the file system, and thus may cause race
+ conditions during access to the file system
+
+Stats:
+ pintos/src/threads/thread.h | 5 +-
+ pintos/src/userprog/exception.c | 17 ++-
+ pintos/src/userprog/process.c | 2 +-
+ pintos/src/userprog/syscall.c | 314 +++++++++++++++++++--------------------
+ pintos/src/userprog/syscall.h | 2 +-
+ 5 files changed, 173 insertions(+), 167 deletions(-)
+
+ Implementation Time: 3 hours
diff --git a/pintos-progos/tests/Algorithm/Diff.pm b/pintos-progos/tests/Algorithm/Diff.pm
new file mode 100644
index 0000000..904c530
--- /dev/null
+++ b/pintos-progos/tests/Algorithm/Diff.pm
@@ -0,0 +1,1713 @@
+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,
+# 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; 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 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 to be as long as possible. In this case I
+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-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 and C 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. But actually, the LCS
+is C:
+
+ 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,
+C, C, OO interface, C, C, C,
+C, and C.
+
+=head2 C
+
+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 may be passed an optional third parameter; this is a CODE
+reference to a key generation function. See L.
+
+ @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
+
+This is just like C except it only returns the length of the
+longest common subsequence. This provides a performance gain of about
+9% compared to C.
+
+=head2 C
+
+Like C 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
+
+ $diff = Algorithm::Diffs->new( \@seq1, \@seq2 );
+ $diff = Algorithm::Diffs->new( \@seq1, \@seq2, \%opts );
+
+C 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, 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 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 for an optional argument is always treated the same
+as if no argument were passed in.
+
+=item C
+
+ $pos = $diff->Next(); # Move forward 1 hunk
+ $pos = $diff->Next( 2 ); # Move forward 2 hunks
+ $pos = $diff->Next(-5); # Move backward 5 hunks
+
+C 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