Merge branch 'riscv' into riscv-proc

This commit is contained in:
Frans Kaashoek 2019-07-02 14:19:31 -04:00
commit 1e4d7065d6
13 changed files with 375 additions and 133 deletions

View file

@ -128,6 +128,7 @@ UPROGS=\
$U/_usertests\
$U/_wc\
$U/_zombie\
$U/_cow\
fs.img: mkfs/mkfs README $(UPROGS)
mkfs/mkfs fs.img README $(UPROGS)

View file

@ -185,9 +185,9 @@ pagetable_t uvmcreate(void);
void uvminit(pagetable_t, uchar *, uint);
uint64 uvmalloc(pagetable_t, uint64, uint64);
uint64 uvmdealloc(pagetable_t, uint64, uint64);
void uvmcopy(pagetable_t, pagetable_t, uint64);
int uvmcopy(pagetable_t, pagetable_t, uint64);
void uvmfree(pagetable_t, uint64);
void mappages(pagetable_t, uint64, uint64, uint64, int);
int mappages(pagetable_t, uint64, uint64, uint64, int);
void unmappages(pagetable_t, uint64, uint64, int);
uint64 walkaddr(pagetable_t, uint64);
int copyout(pagetable_t, uint64, char *, uint64);

View file

@ -47,7 +47,7 @@ kernelvec:
ld ra, 0(sp)
ld sp, 8(sp)
ld gp, 16(sp)
ld tp, 24(sp)
// not this, in case we moved CPUs: ld tp, 24(sp)
ld t0, 32(sp)
ld t1, 40(sp)
ld t2, 48(sp)

View file

@ -111,6 +111,30 @@ found:
return p;
}
// free a proc structure and the data hanging from it,
// including user pages.
// the proc lock must be held.
static void
freeproc(struct proc *p)
{
if(p->kstack)
kfree(p->kstack);
p->kstack = 0;
if(p->tf)
kfree((void*)p->tf);
p->tf = 0;
if(p->pagetable)
proc_freepagetable(p->pagetable, p->sz);
p->pagetable = 0;
p->sz = 0;
p->pid = 0;
p->parent = 0;
p->name[0] = 0;
p->chan = 0;
p->killed = 0;
p->state = UNUSED;
}
// Create a page table for a given process,
// with no users pages, but with trampoline pages.
// Called both when creating a process, and
@ -147,7 +171,8 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz)
{
unmappages(pagetable, TRAMPOLINE, PGSIZE, 0);
unmappages(pagetable, TRAMPOLINE-PGSIZE, PGSIZE, 0);
uvmfree(pagetable, sz);
if(sz > 0)
uvmfree(pagetable, sz);
}
// a user program that calls exec("/init")
@ -226,7 +251,10 @@ fork(void)
}
// Copy user memory from parent to child.
uvmcopy(p->pagetable, np->pagetable, p->sz);
if(uvmcopy(p->pagetable, np->pagetable, p->sz) < 0){
freeproc(np);
return -1;
}
np->sz = p->sz;
np->parent = p;
@ -342,17 +370,7 @@ wait(void)
if(np->state == ZOMBIE){
// Found one.
pid = np->pid;
kfree(np->kstack);
np->kstack = 0;
kfree((void*)np->tf);
np->tf = 0;
proc_freepagetable(np->pagetable, np->sz);
np->pagetable = 0;
np->pid = 0;
np->parent = 0;
np->name[0] = 0;
np->killed = 0;
np->state = UNUSED;
freeproc(np);
release(&np->lock);
release(&p->lock);
return pid;

View file

@ -117,29 +117,29 @@ argstr(int n, char *buf, int max)
return fetchstr(addr, buf, max);
}
extern int sys_chdir(void);
extern int sys_close(void);
extern int sys_dup(void);
extern int sys_exec(void);
extern int sys_exit(void);
extern int sys_fork(void);
extern int sys_fstat(void);
extern int sys_getpid(void);
extern int sys_kill(void);
extern int sys_link(void);
extern int sys_mkdir(void);
extern int sys_mknod(void);
extern int sys_open(void);
extern int sys_pipe(void);
extern int sys_read(void);
extern int sys_sbrk(void);
extern int sys_sleep(void);
extern int sys_unlink(void);
extern int sys_wait(void);
extern int sys_write(void);
extern int sys_uptime(void);
extern uint64 sys_chdir(void);
extern uint64 sys_close(void);
extern uint64 sys_dup(void);
extern uint64 sys_exec(void);
extern uint64 sys_exit(void);
extern uint64 sys_fork(void);
extern uint64 sys_fstat(void);
extern uint64 sys_getpid(void);
extern uint64 sys_kill(void);
extern uint64 sys_link(void);
extern uint64 sys_mkdir(void);
extern uint64 sys_mknod(void);
extern uint64 sys_open(void);
extern uint64 sys_pipe(void);
extern uint64 sys_read(void);
extern uint64 sys_sbrk(void);
extern uint64 sys_sleep(void);
extern uint64 sys_unlink(void);
extern uint64 sys_wait(void);
extern uint64 sys_write(void);
extern uint64 sys_uptime(void);
static int (*syscalls[])(void) = {
static uint64 (*syscalls[])(void) = {
[SYS_fork] sys_fork,
[SYS_exit] sys_exit,
[SYS_wait] sys_wait,

View file

@ -52,7 +52,7 @@ fdalloc(struct file *f)
return -1;
}
int
uint64
sys_dup(void)
{
struct file *f;
@ -66,7 +66,7 @@ sys_dup(void)
return fd;
}
int
uint64
sys_read(void)
{
struct file *f;
@ -78,7 +78,7 @@ sys_read(void)
return fileread(f, p, n);
}
int
uint64
sys_write(void)
{
struct file *f;
@ -91,7 +91,7 @@ sys_write(void)
return filewrite(f, p, n);
}
int
uint64
sys_close(void)
{
int fd;
@ -104,7 +104,7 @@ sys_close(void)
return 0;
}
int
uint64
sys_fstat(void)
{
struct file *f;
@ -116,7 +116,7 @@ sys_fstat(void)
}
// Create the path new as a link to the same inode as old.
int
uint64
sys_link(void)
{
char name[DIRSIZ], new[MAXPATH], old[MAXPATH];
@ -182,7 +182,7 @@ isdirempty(struct inode *dp)
}
//PAGEBREAK!
int
uint64
sys_unlink(void)
{
struct inode *ip, *dp;
@ -284,7 +284,7 @@ create(char *path, short type, short major, short minor)
return ip;
}
int
uint64
sys_open(void)
{
char path[MAXPATH];
@ -347,7 +347,7 @@ sys_open(void)
return fd;
}
int
uint64
sys_mkdir(void)
{
char path[MAXPATH];
@ -363,7 +363,7 @@ sys_mkdir(void)
return 0;
}
int
uint64
sys_mknod(void)
{
struct inode *ip;
@ -383,7 +383,7 @@ sys_mknod(void)
return 0;
}
int
uint64
sys_chdir(void)
{
char path[MAXPATH];
@ -408,7 +408,7 @@ sys_chdir(void)
return 0;
}
int
uint64
sys_exec(void)
{
char path[MAXPATH], *argv[MAXARG];
@ -446,7 +446,7 @@ sys_exec(void)
return ret;
}
int
uint64
sys_pipe(void)
{
uint64 fdarray; // user pointer to array of two integers

View file

@ -7,32 +7,32 @@
#include "spinlock.h"
#include "proc.h"
int
uint64
sys_exit(void)
{
exit();
return 0; // not reached
}
int
uint64
sys_getpid(void)
{
return myproc()->pid;
}
int
uint64
sys_fork(void)
{
return fork();
}
int
uint64
sys_wait(void)
{
return wait();
}
int
uint64
sys_sbrk(void)
{
int addr;
@ -46,7 +46,7 @@ sys_sbrk(void)
return addr;
}
int
uint64
sys_sleep(void)
{
int n;
@ -67,7 +67,7 @@ sys_sleep(void)
return 0;
}
int
uint64
sys_kill(void)
{
int pid;
@ -79,7 +79,7 @@ sys_kill(void)
// return how many clock tick interrupts have occurred
// since start.
int
uint64
sys_uptime(void)
{
uint xticks;

View file

@ -50,8 +50,6 @@ usertrap(void)
// save user program counter.
p->tf->epc = r_sepc();
intr_on();
if(r_scause() == 8){
// system call
@ -59,11 +57,15 @@ usertrap(void)
// but we want to return to the next instruction.
p->tf->epc += 4;
// an interrupt will change sstatus &c registers,
// so don't enable until done with those registers.
intr_on();
syscall();
} else if((which_dev = devintr()) != 0){
// ok
} else {
printf("usertrap(): unexpected scause 0x%p pid=%d\n", r_scause(), p->pid);
printf("usertrap(): unexpected scause %p pid=%d\n", r_scause(), p->pid);
printf(" sepc=%p stval=%p\n", r_sepc(), r_stval());
p->killed = 1;
}
@ -121,12 +123,14 @@ usertrapret(void)
((void (*)(uint64,uint64))TRAMPOLINE)(TRAMPOLINE - PGSIZE, satp);
}
// interrupts and exceptions from kernel code go here,
// interrupts and exceptions from kernel code go here via kernelvec,
// on whatever the current kernel stack is.
// must be 4-byte aligned to fit in stvec.
void
kerneltrap()
{
int which_dev = 0;
uint64 sepc = r_sepc();
uint64 sstatus = r_sstatus();
uint64 scause = r_scause();
@ -135,11 +139,20 @@ kerneltrap()
if(intr_get() != 0)
panic("kerneltrap: interrupts enabled");
if(devintr() == 0){
printf("scause 0x%p\n", scause);
if((which_dev = devintr()) == 0){
printf("scause %p\n", scause);
printf("sepc=%p stval=%p\n", r_sepc(), r_stval());
panic("kerneltrap");
}
// give up the CPU if this is a timer interrupt.
if(which_dev == 2 && myproc() != 0 && myproc()->state == RUNNING)
yield();
// the yield() may have caused some traps to occur,
// so restore trap registers for use by kernelvec.S's sepc instruction.
w_sepc(sepc);
w_sstatus(sstatus);
}
// check if it's an external interrupt or software interrupt,

View file

@ -97,8 +97,8 @@ walk(pagetable_t pagetable, uint64 va, int alloc)
}
// Look up a virtual address, return the physical address,
// Can only be used to look up user pages.
// or 0 if not mapped.
// Can only be used to look up user pages.
uint64
walkaddr(pagetable_t pagetable, uint64 va)
{
@ -119,8 +119,9 @@ walkaddr(pagetable_t pagetable, uint64 va)
// Create PTEs for virtual addresses starting at va that refer to
// physical addresses starting at pa. va and size might not
// be page-aligned.
void
// be page-aligned. Returns 0 on success, -1 if walk() couldn't
// allocate a needed page-table page.
int
mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
{
uint64 a, last;
@ -130,7 +131,7 @@ mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
last = PGROUNDDOWN(va + size - 1);
for(;;){
if((pte = walk(pagetable, a, 1)) == 0)
panic("mappages: walk");
return -1;
if(*pte & PTE_V)
panic("remap");
*pte = PA2PTE(pa) | perm | PTE_V;
@ -139,6 +140,7 @@ mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
a += PGSIZE;
pa += PGSIZE;
}
return 0;
}
// Remove mappings from a page table. The mappings in
@ -222,7 +224,11 @@ uvmalloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
return 0;
}
memset(mem, 0, PGSIZE);
mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U);
if(mappages(pagetable, a, PGSIZE, (uint64)mem, PTE_W|PTE_X|PTE_R|PTE_U) != 0){
kfree(mem);
uvmdealloc(pagetable, a, oldsz);
return 0;
}
}
return newsz;
}
@ -273,7 +279,9 @@ uvmfree(pagetable_t pagetable, uint64 sz)
// its memory into a child's page table.
// Copies both the page table and the
// physical memory.
void
// returns 0 on success, -1 on failure.
// frees any allocated pages on failure.
int
uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
{
pte_t *pte;
@ -289,10 +297,18 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz)
pa = PTE2PA(*pte);
flags = PTE_FLAGS(*pte);
if((mem = kalloc()) == 0)
panic("uvmcopy: kalloc failed");
goto err;
memmove(mem, (char*)pa, PGSIZE);
mappages(new, i, PGSIZE, (uint64)mem, flags);
if(mappages(new, i, PGSIZE, (uint64)mem, flags) != 0){
kfree(mem);
goto err;
}
}
return 0;
err:
unmappages(new, 0, i, 1);
return -1;
}
// Copy from kernel to user.

5
runoff
View file

@ -12,7 +12,10 @@ pad()
# create formatted (numbered) files
mkdir -p fmt
rm -f fmt/*
mkdir -p fmt/kernel
mkdir -p fmt/user
rm -f fmt/kernel/*
rm -f fmt/user/*
cp README fmt
echo > fmt/blank
files=`grep -v '^#' runoff.list | awk '{print $1}'`

View file

@ -1,76 +1,64 @@
# basic headers
types.h
param.h
memlayout.h
defs.h
x86.h
asm.h
mmu.h
elf.h
date.h
kernel/types.h
kernel/param.h
kernel/memlayout.h
kernel/defs.h
kernel/riscv.h
kernel/elf.h
kernel/date.h
# entering xv6
entry.S
entryother.S
main.c
kernel/entry.S
kernel/main.c
# locks
spinlock.h
spinlock.c
kernel/spinlock.h
kernel/spinlock.c
# processes
vm.c
proc.h
proc.c
swtch.S
kalloc.c
kernel/vm.c
kernel/proc.h
kernel/proc.c
kernel/swtch.S
kernel/kalloc.c
# system calls
traps.h
vectors.pl
trapasm.S
trap.c
syscall.h
syscall.c
sysproc.c
user/usys.pl
kernel/trap.c
kernel/syscall.h
kernel/syscall.c
kernel/sysproc.c
# file system
buf.h
sleeplock.h
fcntl.h
stat.h
fs.h
file.h
ide.c
bio.c
sleeplock.c
log.c
fs.c
file.c
sysfile.c
exec.c
kernel/buf.h
kernel/sleeplock.h
kernel/fcntl.h
kernel/stat.h
kernel/fs.h
kernel/file.h
kernel/virtio_disk.c
kernel/bio.c
kernel/sleeplock.c
kernel/log.c
kernel/fs.c
kernel/file.c
kernel/sysfile.c
kernel/exec.c
# pipes
pipe.c
kernel/pipe.c
# string operations
string.c
kernel/string.c
# low-level hardware
mp.h
mp.c
lapic.c
ioapic.c
kbd.h
kbd.c
console.c
uart.c
kernel/uart.c
# user-level
initcode.S
usys.S
init.c
sh.c
user/initcode.S
user/usys.S
user/init.c
user/sh.c
# link
kernel.ld
kernel/kernel.ld

196
user/cow.c Normal file
View file

@ -0,0 +1,196 @@
//
// tests for copy-on-write fork() assignment.
//
#include "kernel/types.h"
#include "kernel/memlayout.h"
#include "user/user.h"
// allocate more than half of physical memory,
// then fork. this will fail in the default
// kernel, which does not support copy-on-write.
void
simpletest()
{
uint64 phys_size = PHYSTOP - KERNBASE;
int sz = (phys_size / 3) * 2;
printf(1, "simple: ");
char *p = sbrk(sz);
if(p == (char*)0xffffffffffffffffL){
printf(1, "sbrk(%d) failed\n", sz);
exit();
}
for(char *q = p; q < p + sz; q += 4096){
*(int*)q = getpid();
}
int pid = fork();
if(pid < 0){
printf(1, "fork() failed\n");
exit();
}
if(pid == 0)
exit();
wait();
if(sbrk(-sz) == (char*)0xffffffffffffffffL){
printf(1, "sbrk(-%d) failed\n", sz);
exit();
}
printf(1, "ok\n");
}
// three processes all write COW memory.
// this causes more than half of physical memory
// to be allocated, so it also checks whether
// copied pages are freed.
void
threetest()
{
uint64 phys_size = PHYSTOP - KERNBASE;
int sz = phys_size / 4;
int pid1, pid2;
printf(1, "three: ");
char *p = sbrk(sz);
if(p == (char*)0xffffffffffffffffL){
printf(1, "sbrk(%d) failed\n", sz);
exit();
}
pid1 = fork();
if(pid1 < 0){
printf(1, "fork failed\n");
exit();
}
if(pid1 == 0){
pid2 = fork();
if(pid2 < 0){
printf(1, "fork failed");
exit();
}
if(pid2 == 0){
for(char *q = p; q < p + (sz/5)*4; q += 4096){
*(int*)q = getpid();
}
for(char *q = p; q < p + (sz/5)*4; q += 4096){
if(*(int*)q != getpid()){
printf(1, "wrong content\n");
exit();
}
}
exit();
}
for(char *q = p; q < p + (sz/2); q += 4096){
*(int*)q = 9999;
}
exit();
}
for(char *q = p; q < p + sz; q += 4096){
*(int*)q = getpid();
}
wait();
sleep(1);
for(char *q = p; q < p + sz; q += 4096){
if(*(int*)q != getpid()){
printf(1, "wrong content\n");
exit();
}
}
if(sbrk(-sz) == (char*)0xffffffffffffffffL){
printf(1, "sbrk(-%d) failed\n", sz);
exit();
}
printf(1, "ok\n");
}
char junk1[4096];
int fds[2];
char junk2[4096];
char buf[4096];
char junk3[4096];
// test whether copyout() simulates COW faults.
void
filetest()
{
int parent = getpid();
printf(1, "file test: ");
buf[0] = 99;
for(int i = 0; i < 4; i++){
if(pipe(fds) != 0){
printf(1, "pipe() failed\n");
exit();
}
int pid = fork();
if(pid < 0){
printf(1, "fork failed\n");
exit();
}
if(pid == 0){
sleep(1);
if(read(fds[0], buf, sizeof(i)) != sizeof(i)){
printf(1, "read failed\n");
kill(parent);
exit();
}
sleep(1);
int j = *(int*)buf;
if(j != i){
printf(1, "read the wrong value\n");
kill(parent);
exit();
}
exit();
}
if(write(fds[1], &i, sizeof(i)) != sizeof(i)){
printf(1, "write failed\n");
exit();
}
}
for(int i = 0; i < 4; i++)
wait();
if(buf[0] != 99){
printf(1, "child overwrote parent\n");
exit();
}
printf(1, "ok\n");
}
int
main(int argc, char *argv[])
{
simpletest();
// check that the first simpletest() freed the physical memory.
simpletest();
threetest();
threetest();
threetest();
filetest();
printf(1, "ALL COW TESTS PASSED\n");
exit();
}

View file

@ -1438,6 +1438,13 @@ sbrktest(void)
printf(stdout, "sbrk test\n");
oldbrk = sbrk(0);
// does sbrk() return the expected failure value?
a = sbrk(1024*1024*1024);
if(a != (char*)0xffffffffffffffffL){
printf(stdout, "sbrk(<toomuch>) returned %p\n", a);
exit();
}
// can one sbrk() less than a page?
a = sbrk(0);
for(i = 0; i < 5000; i++){
@ -1466,7 +1473,7 @@ sbrktest(void)
// can one grow address space to something big?
a = sbrk(0);
amt = (BIG) - (uint64)a;
amt = BIG - (uint64)a;
p = sbrk(amt);
if (p != a) {
printf(stdout, "sbrk test failed to grow big address space; enough phys mem?\n");
@ -1478,7 +1485,7 @@ sbrktest(void)
// can one de-allocate?
a = sbrk(0);
c = sbrk(-4096);
if(c == (char*)0xffffffff){
if(c == (char*)0xffffffffffffffffL){
printf(stdout, "sbrk could not deallocate\n");
exit();
}
@ -1551,7 +1558,7 @@ sbrktest(void)
kill(pids[i]);
wait();
}
if(c == (char*)0xffffffff){
if(c == (char*)0xffffffffffffffffL){
printf(stdout, "failed sbrk leaked memory\n");
exit();
}