Merge branch 'riscv' of g.csail.mit.edu:xv6-dev into riscv

This commit is contained in:
Robert Morris 2019-07-26 04:53:59 -04:00
commit 8d30e21b59
3 changed files with 250 additions and 10 deletions

View file

@ -133,9 +133,9 @@ kvmpa(uint64 va)
pte = walk(kernel_pagetable, va, 0);
if(pte == 0)
panic("kernelpa");
panic("kvmpa");
if((*pte & PTE_V) == 0)
panic("kernelpa");
panic("kvmpa");
pa = PTE2PA(*pte);
return pa+off;
}
@ -343,7 +343,7 @@ uvmclear(pagetable_t pagetable, uint64 va)
pte = walk(pagetable, va, 0);
if(pte == 0)
panic("clearpteu");
panic("uvmclear");
*pte &= ~PTE_U;
}

196
labs/syscall.html Normal file
View file

@ -0,0 +1,196 @@
<html>
<head>
<title>Lab: system calls</title>
<link rel="stylesheet" href="homework.css" type="text/css" />
</head>
<body>
<h1>Lab: system calls</h1>
This lab makes you familiar with the implementation of system calls.
In particular, you will implement a new system call: <tt>alarm</tt>.
<h2>Warmup: system call tracing</h2>
<p>In this exercise you will modify the xv6 kernel to print out a line
for each system call invocation. It is enough to print the name of the
system call and the return value; you don't need to print the system
call arguments.
<p>
When you're done, you should see output like this when booting
xv6:
<pre>
...
fork -> 2
exec -> 0
open -> 3
close -> 0
$write -> 1
write -> 1
</pre>
<p>
That's init forking and execing sh, sh making sure only two file descriptors are
open, and sh writing the $ prompt. (Note: the output of the shell and the
system call trace are intermixed, because the shell uses the write syscall to
print its output.)
<p> Hint: modify the syscall() function in kernel/syscall.c.
<p>Run the programs you wrote in the lab and inspect the system call
trace. Are there many system calls? Which systems calls correspond
to code in the applications you wrote above?
<p>Optional: print the system call arguments.
<h2>alarm</h2>
<p>
In this exercise you'll add a feature to xv6 that periodically alerts
a process as it uses CPU time. This might be useful for compute-bound
processes that want to limit how much CPU time they chew up, or for
processes that want to compute but also want to take some periodic
action. More generally, you'll be implementing a primitive form of
user-level interrupt/fault handlers; you could use something similar
to handle page faults in the application, for example.
<p>
You should add a new <tt>alarm(interval, handler)</tt> system call.
If an application calls <tt>alarm(n, fn)</tt>, then after every
<tt>n</tt> "ticks" of CPU time that the program consumes, the kernel
will cause application function
<tt>fn</tt> to be called. When <tt>fn</tt> returns, the application
will resume where it left off. A tick is a fairly arbitrary unit of
time in xv6, determined by how often a hardware timer generates
interrupts.
<p>
You should put the following example program in <tt>user/alarmtest.c</tt>:
<pre>
#include "kernel/param.h"
#include "kernel/types.h"
#include "kernel/stat.h"
#include "user/user.h"
void periodic();
int
main(int argc, char *argv[])
{
int i;
printf(1, "alarmtest starting\n");
alarm(10, periodic);
for(i = 0; i < 25*500000; i++){
if((i % 250000) == 0)
write(2, ".", 1);
}
exit();
}
void
periodic()
{
printf(1, "alarm!\n");
}
</pre>
The program calls <tt>alarm(10, periodic)</tt> to ask the kernel to
force a call to <tt>periodic()</tt> every 10 ticks, and then spins for
a while.
After you have implemented the <tt>alarm()</tt> system call in the kernel,
<tt>alarmtest</tt> should produce output like this:
<pre>
$ alarmtest
alarmtest starting
.....alarm!
....alarm!
.....alarm!
......alarm!
.....alarm!
....alarm!
....alarm!
......alarm!
.....alarm!
...alarm!
...$
</pre>
<p>
<p>
(If you only see one "alarm!", try increasing the number of iterations in
<tt>alarmtest.c</tt> by 10x.)
Here are some hints:
<ul>
<li>You'll need to modify the Makefile to cause <tt>alarmtest.c</tt>
to be compiled as an xv6 user program.
<li>The right declaration to put in <tt>user/user.h</tt> is:
<pre>
int alarm(int ticks, void (*handler)());
</pre>
<li>Update <tt>kernel/syscall.h</tt> and <tt>user/usys.S</tt> to
allow <tt>alarmtest</tt> to invoke the alarm system call.
<li>Your <tt>sys_alarm()</tt> should store the alarm interval and the
pointer to the handler function in new fields in the <tt>proc</tt>
structure; see <tt>kernel/proc.h</tt>.
<li>
You'll need to keep track of how many ticks have passed since
the last call
(or are left until the next call) to a process's alarm handler;
you'll need a new field in <tt>struct&nbsp;proc</tt> for this too.
You can initialize <tt>proc</tt> fields in <tt>allocproc()</tt>
in <tt>proc.c</tt>.
<li>
Every tick, the hardware clock forces an interrupt, which
is handled in <tt>usertrap()</tt>; you should add some code here.
<li>
You only want to manipulate a process's alarm ticks if there's a
a timer interrupt; you want something like
<pre>
if(which_dev == 2) ..
</pre>
<p>
In your usertrap, when a process's alarm interval expires,
you'll want to cause it to execute its handler. How can you do that?
<li>
You need to arrange things so that, when the handler returns,
the process resumes executing where it left off. How can you do that?
<li>
You can see the assembly code for the alarmtest program in alarmtest.asm.
<li>
It will be easier to look at traps with gdb if you tell qemu to use
only one CPU, which you can do by running
<pre>
make CPUS=1 qemu
</pre>
<li>
It's OK if your solution doesn't save the caller-saved user registers
when calling the handler.
<ul>
<p>
Optional challenges: 1) Save and restore the caller-saved user registers around the
call to handler. 2) Prevent re-entrant calls to the handler -- if a handler
hasn't returned yet, don't call it again. 3) Assuming your code doesn't
check that <tt>tf->esp</tt> is valid, implement a security attack on the
kernel that exploits your alarm handler calling code.

View file

@ -109,7 +109,7 @@ initial file system. You just ran one of them: <tt>ls</tt>.
$ make qemu
...
init: starting sh
$ sleep 5
$ sleep 10
(waits for a little while)
$
</pre>
@ -178,16 +178,60 @@ initial file system. You just ran one of them: <tt>ls</tt>.
<li>Don't recurse into "." and "..".
</ul>
<p>Optional: support regular expressions in name matching. Grep has some
primitive support for regular expressions.
<h2>xargs</h2>
<p>Write a simple version of the UNIX xargs program: read lines from
standard in and run a command for each line, supplying the line as
arguments to the command. The following example illustrates xarg's
behavior:
<pre>
$ xargs echo bye
hello too
bye hello too
<ctrl-d>
$
</pre>
Note that the command here is "echo bye" and the additional
arguments are "hello too", making the command "echo bye hello too",
which outputs "bye hello too".
<p>xargs and find combine well:
<pre>
find . b | xargs grep hello
</pre>
will run "grep hello" on each file named b in the directories below ".".
<p>Some hints:
<ul>
<li>Use <tt>fork</tt> and <tt>exec</tt> system call to invoke the
command on each line of input. Use <tt>wait</tt> in the parent
to wait for the child to complete running the command.
<li>Read from stdin a character at the time until the newline
character ('\n').
<li>kernel/param.h declares MAXARG, which may be useful if you need
to declare an argv.
</ul>
<h2>Optional: modify the shell</h2>
<p>Modify the shell to support wait.
<p>Modify the shell to support lists of commands, separated by ";"
There are endless ways in which the shell could be extended. Here are
some suggestions:
<p>Modify the shell to support sub-shells by implementing "(" and ")"
<p>Modify the shell to allow users to edit the command line
<ul>
<li>Modify the shell to support wait.
<li>Modify the shell to support lists of commands, separated by ";"
<li>Modify the shell to support sub-shells by implementing "(" and ")"
<li>Modify the shell to allow users to edit the command line
</ul>
</body>
</html>