summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.editorconfig22
-rw-r--r--Makefile13
-rw-r--r--kernel/bio.c8
-rw-r--r--kernel/buf.h1
-rw-r--r--kernel/console.c10
-rw-r--r--kernel/defs.h3
-rw-r--r--kernel/exec.c7
-rw-r--r--kernel/fcntl.h1
-rw-r--r--kernel/fs.c12
-rw-r--r--kernel/kernel.ld36
-rw-r--r--kernel/memlayout.h2
-rw-r--r--kernel/pipe.c6
-rw-r--r--kernel/plic.c16
-rw-r--r--kernel/proc.c32
-rw-r--r--kernel/proc.h8
-rw-r--r--kernel/riscv.h1
-rw-r--r--kernel/spinlock.c16
-rw-r--r--kernel/start.c1
-rw-r--r--kernel/syscall.c18
-rw-r--r--kernel/sysfile.c4
-rw-r--r--kernel/trampoline.S12
-rw-r--r--kernel/trap.c29
-rw-r--r--kernel/uart.c121
-rw-r--r--kernel/vm.c11
-rw-r--r--user/alarmtest.c88
-rw-r--r--user/cat.c8
-rw-r--r--user/grind.c333
-rw-r--r--user/sh.c2
-rw-r--r--user/ulib.c31
-rw-r--r--user/user.h2
-rw-r--r--user/usertests.c471
31 files changed, 1074 insertions, 251 deletions
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..c47611e
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,22 @@
+; https://editorconfig.org
+
+root = true
+
+[*]
+end_of_line = lf
+insert_final_newline = true
+indent_style = space
+indent_size = 4
+
+[*.{c,h}]
+indent_size = 2
+
+[*.S]
+indent_size = 8
+
+[*.ld]
+indent_size = 2
+
+[Makefile]
+indent_style = tab
+indent_size = 8
diff --git a/Makefile b/Makefile
index a398c5a..96f1c14 100644
--- a/Makefile
+++ b/Makefile
@@ -40,8 +40,10 @@ TOOLPREFIX := $(shell if riscv64-unknown-elf-objdump -i 2>&1 | grep 'elf64-big'
then echo 'riscv64-unknown-elf-'; \
elif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
then echo 'riscv64-linux-gnu-'; \
+ elif riscv64-unknown-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
+ then echo 'riscv64-unknown-linux-gnu-'; \
else echo "***" 1>&2; \
- echo "*** Error: Couldn't find an riscv64 version of GCC/binutils." 1>&2; \
+ echo "*** Error: Couldn't find a riscv64 version of GCC/binutils." 1>&2; \
echo "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \
echo "***" 1>&2; exit 1; fi)
endif
@@ -104,7 +106,7 @@ $U/_forktest: $U/forktest.o $(ULIB)
$(LD) $(LDFLAGS) -N -e main -Ttext 0 -o $U/_forktest $U/forktest.o $U/ulib.o $U/usys.o
$(OBJDUMP) -S $U/_forktest > $U/forktest.asm
-mkfs/mkfs: mkfs/mkfs.c $K/fs.h
+mkfs/mkfs: mkfs/mkfs.c $K/fs.h $K/param.h
gcc -Werror -Wall -I. -o mkfs/mkfs mkfs/mkfs.c
# Prevent deletion of intermediate files, e.g. cat.o, after first build, so
@@ -127,6 +129,7 @@ UPROGS=\
$U/_sh\
$U/_stressfs\
$U/_usertests\
+ $U/_grind\
$U/_wc\
$U/_zombie\
@@ -153,9 +156,9 @@ ifndef CPUS
CPUS := 3
endif
-QEMUEXTRA = -drive file=fs1.img,if=none,format=raw,id=x1 -device virtio-blk-device,drive=x1,bus=virtio-mmio-bus.1
-QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 3G -smp $(CPUS) -nographic
-QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
+QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 128M -smp $(CPUS) -nographic
+QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0
+QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
qemu: $K/kernel fs.img
$(QEMU) $(QEMUOPTS)
diff --git a/kernel/bio.c b/kernel/bio.c
index a1074f2..60d91a6 100644
--- a/kernel/bio.c
+++ b/kernel/bio.c
@@ -28,7 +28,8 @@ struct {
struct buf buf[NBUF];
// Linked list of all buffers, through prev/next.
- // head.next is most recently used.
+ // Sorted by how recently the buffer was used.
+ // head.next is most recent, head.prev is least.
struct buf head;
} bcache;
@@ -71,7 +72,8 @@ bget(uint dev, uint blockno)
}
}
- // Not cached; recycle an unused buffer.
+ // Not cached.
+ // Recycle the least recently used (LRU) unused buffer.
for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
if(b->refcnt == 0) {
b->dev = dev;
@@ -110,7 +112,7 @@ bwrite(struct buf *b)
}
// Release a locked buffer.
-// Move to the head of the MRU list.
+// Move to the head of the most-recently-used list.
void
brelse(struct buf *b)
{
diff --git a/kernel/buf.h b/kernel/buf.h
index 4a3a39d..4616e9e 100644
--- a/kernel/buf.h
+++ b/kernel/buf.h
@@ -7,7 +7,6 @@ struct buf {
uint refcnt;
struct buf *prev; // LRU cache list
struct buf *next;
- struct buf *qnext; // disk queue
uchar data[BSIZE];
};
diff --git a/kernel/console.c b/kernel/console.c
index 87a83ff..2b1ed3c 100644
--- a/kernel/console.c
+++ b/kernel/console.c
@@ -27,6 +27,8 @@
//
// send one character to the uart.
+// called by printf, and to echo input characters,
+// but not from write().
//
void
consputc(int c)
@@ -40,9 +42,9 @@ consputc(int c)
if(c == BACKSPACE){
// if the user typed backspace, overwrite with a space.
- uartputc('\b'); uartputc(' '); uartputc('\b');
+ uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b');
} else {
- uartputc(c);
+ uartputc_sync(c);
}
}
@@ -70,11 +72,11 @@ consolewrite(int user_src, uint64 src, int n)
char c;
if(either_copyin(&c, user_src, src+i, 1) == -1)
break;
- consputc(c);
+ uartputc(c);
}
release(&cons.lock);
- return n;
+ return i;
}
//
diff --git a/kernel/defs.h b/kernel/defs.h
index f893d28..4eedd89 100644
--- a/kernel/defs.h
+++ b/kernel/defs.h
@@ -52,6 +52,7 @@ struct inode* nameiparent(char*, char*);
int readi(struct inode*, int, uint64, uint, uint);
void stati(struct inode*, struct stat*);
int writei(struct inode*, int, uint64, uint, uint);
+void itrunc(struct inode*);
// ramdisk.c
void ramdiskinit(void);
@@ -149,6 +150,7 @@ void usertrapret(void);
void uartinit(void);
void uartintr(void);
void uartputc(int);
+void uartputc_sync(int);
int uartgetc(void);
// vm.c
@@ -173,7 +175,6 @@ int copyinstr(pagetable_t, char *, uint64, uint64);
// plic.c
void plicinit(void);
void plicinithart(void);
-uint64 plic_pending(void);
int plic_claim(void);
void plic_complete(int);
diff --git a/kernel/exec.c b/kernel/exec.c
index 74ef654..1077ac0 100644
--- a/kernel/exec.c
+++ b/kernel/exec.c
@@ -97,7 +97,7 @@ exec(char *path, char **argv)
// arguments to user main(argc, argv)
// argc is returned via the system call return
// value, which goes in a0.
- p->tf->a1 = sp;
+ p->trapframe->a1 = sp;
// Save program name for debugging.
for(last=s=path; *s; s++)
@@ -109,9 +109,10 @@ exec(char *path, char **argv)
oldpagetable = p->pagetable;
p->pagetable = pagetable;
p->sz = sz;
- p->tf->epc = elf.entry; // initial program counter = main
- p->tf->sp = sp; // initial stack pointer
+ p->trapframe->epc = elf.entry; // initial program counter = main
+ p->trapframe->sp = sp; // initial stack pointer
proc_freepagetable(oldpagetable, oldsz);
+
return argc; // this ends up in a0, the first argument to main(argc, argv)
bad:
diff --git a/kernel/fcntl.h b/kernel/fcntl.h
index d565483..44861b9 100644
--- a/kernel/fcntl.h
+++ b/kernel/fcntl.h
@@ -2,3 +2,4 @@
#define O_WRONLY 0x001
#define O_RDWR 0x002
#define O_CREATE 0x200
+#define O_TRUNC 0x400
diff --git a/kernel/fs.c b/kernel/fs.c
index 53586d5..ec68cd7 100644
--- a/kernel/fs.c
+++ b/kernel/fs.c
@@ -22,7 +22,6 @@
#include "file.h"
#define min(a, b) ((a) < (b) ? (a) : (b))
-static void itrunc(struct inode*);
// there should be one superblock per disk device, but we run with
// only one device
struct superblock sb;
@@ -406,11 +405,8 @@ bmap(struct inode *ip, uint bn)
}
// Truncate inode (discard contents).
-// Only called when the inode has no links
-// to it (no directory entries referring to it)
-// and has no in-memory reference to it (is
-// not an open file or current directory).
-static void
+// Caller must hold ip->lock.
+void
itrunc(struct inode *ip)
{
int i, j;
@@ -463,7 +459,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n)
struct buf *bp;
if(off > ip->size || off + n < off)
- return -1;
+ return 0;
if(off + n > ip->size)
n = ip->size - off;
@@ -476,7 +472,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n)
}
brelse(bp);
}
- return n;
+ return tot;
}
// Write data to inode.
diff --git a/kernel/kernel.ld b/kernel/kernel.ld
index acc3c8e..ee04f22 100644
--- a/kernel/kernel.ld
+++ b/kernel/kernel.ld
@@ -8,25 +8,37 @@ SECTIONS
* where qemu's -kernel jumps.
*/
. = 0x80000000;
- .text :
- {
- *(.text)
+
+ .text : {
+ *(.text .text.*)
. = ALIGN(0x1000);
+ _trampoline = .;
*(trampsec)
+ . = ALIGN(0x1000);
+ ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page");
+ PROVIDE(etext = .);
}
- . = ALIGN(0x1000);
- PROVIDE(etext = .);
+ .rodata : {
+ . = ALIGN(16);
+ *(.srodata .srodata.*) /* do not need to distinguish this from .rodata */
+ . = ALIGN(16);
+ *(.rodata .rodata.*)
+ }
- /*
- * make sure end is after data and bss.
- */
.data : {
- *(.data)
+ . = ALIGN(16);
+ *(.sdata .sdata.*) /* do not need to distinguish this from .data */
+ . = ALIGN(16);
+ *(.data .data.*)
}
+
.bss : {
- *(.bss)
- *(.sbss*)
- PROVIDE(end = .);
+ . = ALIGN(16);
+ *(.sbss .sbss.*) /* do not need to distinguish this from .bss */
+ . = ALIGN(16);
+ *(.bss .bss.*)
}
+
+ PROVIDE(end = .);
}
diff --git a/kernel/memlayout.h b/kernel/memlayout.h
index 8ffd538..b6e595d 100644
--- a/kernel/memlayout.h
+++ b/kernel/memlayout.h
@@ -62,6 +62,6 @@
// fixed-size stack
// expandable heap
// ...
-// TRAPFRAME (p->tf, used by the trampoline)
+// TRAPFRAME (p->trapframe, used by the trampoline)
// TRAMPOLINE (the same page as in the kernel)
#define TRAPFRAME (TRAMPOLINE - PGSIZE)
diff --git a/kernel/pipe.c b/kernel/pipe.c
index e358283..7ed402d 100644
--- a/kernel/pipe.c
+++ b/kernel/pipe.c
@@ -83,7 +83,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n)
acquire(&pi->lock);
for(i = 0; i < n; i++){
while(pi->nwrite == pi->nread + PIPESIZE){ //DOC: pipewrite-full
- if(pi->readopen == 0 || myproc()->killed){
+ if(pi->readopen == 0 || pr->killed){
release(&pi->lock);
return -1;
}
@@ -96,7 +96,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n)
}
wakeup(&pi->nread);
release(&pi->lock);
- return n;
+ return i;
}
int
@@ -108,7 +108,7 @@ piperead(struct pipe *pi, uint64 addr, int n)
acquire(&pi->lock);
while(pi->nread == pi->nwrite && pi->writeopen){ //DOC: pipe-empty
- if(myproc()->killed){
+ if(pr->killed){
release(&pi->lock);
return -1;
}
diff --git a/kernel/plic.c b/kernel/plic.c
index b569492..5acba39 100644
--- a/kernel/plic.c
+++ b/kernel/plic.c
@@ -28,26 +28,11 @@ plicinithart(void)
*(uint32*)PLIC_SPRIORITY(hart) = 0;
}
-// return a bitmap of which IRQs are waiting
-// to be served.
-uint64
-plic_pending(void)
-{
- uint64 mask;
-
- //mask = *(uint32*)(PLIC + 0x1000);
- //mask |= (uint64)*(uint32*)(PLIC + 0x1004) << 32;
- mask = *(uint64*)PLIC_PENDING;
-
- return mask;
-}
-
// ask the PLIC what interrupt we should serve.
int
plic_claim(void)
{
int hart = cpuid();
- //int irq = *(uint32*)(PLIC + 0x201004);
int irq = *(uint32*)PLIC_SCLAIM(hart);
return irq;
}
@@ -57,6 +42,5 @@ void
plic_complete(int irq)
{
int hart = cpuid();
- //*(uint32*)(PLIC + 0x201004) = irq;
*(uint32*)PLIC_SCLAIM(hart) = irq;
}
diff --git a/kernel/proc.c b/kernel/proc.c
index bbf3af0..cf7f260 100644
--- a/kernel/proc.c
+++ b/kernel/proc.c
@@ -20,6 +20,7 @@ static void wakeup1(struct proc *chan);
extern char trampoline[]; // trampoline.S
+// initialize the proc table at boot time.
void
procinit(void)
{
@@ -106,7 +107,7 @@ found:
p->pid = allocpid();
// Allocate a trapframe page.
- if((p->tf = (struct trapframe *)kalloc()) == 0){
+ if((p->trapframe = (struct trapframe *)kalloc()) == 0){
release(&p->lock);
return 0;
}
@@ -116,7 +117,7 @@ found:
// Set up new context to start executing at forkret,
// which returns to user space.
- memset(&p->context, 0, sizeof p->context);
+ memset(&p->context, 0, sizeof(p->context));
p->context.ra = (uint64)forkret;
p->context.sp = p->kstack + PGSIZE;
@@ -129,9 +130,9 @@ found:
static void
freeproc(struct proc *p)
{
- if(p->tf)
- kfree((void*)p->tf);
- p->tf = 0;
+ if(p->trapframe)
+ kfree((void*)p->trapframe);
+ p->trapframe = 0;
if(p->pagetable)
proc_freepagetable(p->pagetable, p->sz);
p->pagetable = 0;
@@ -145,8 +146,8 @@ freeproc(struct proc *p)
p->state = UNUSED;
}
-// Create a page table for a given process,
-// with no user pages, but with trampoline pages.
+// Create a user page table for a given process,
+// with no user memory, but with trampoline pages.
pagetable_t
proc_pagetable(struct proc *p)
{
@@ -164,7 +165,7 @@ proc_pagetable(struct proc *p)
// map the trapframe just below TRAMPOLINE, for trampoline.S.
mappages(pagetable, TRAPFRAME, PGSIZE,
- (uint64)(p->tf), PTE_R | PTE_W);
+ (uint64)(p->trapframe), PTE_R | PTE_W);
return pagetable;
}
@@ -176,8 +177,7 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz)
{
uvmunmap(pagetable, TRAMPOLINE, PGSIZE, 0);
uvmunmap(pagetable, TRAPFRAME, PGSIZE, 0);
- if(sz > 0)
- uvmfree(pagetable, sz);
+ uvmfree(pagetable, sz);
}
// a user program that calls exec("/init")
@@ -207,8 +207,8 @@ userinit(void)
p->sz = PGSIZE;
// prepare for the very first "return" from kernel to user.
- p->tf->epc = 0; // user program counter
- p->tf->sp = PGSIZE; // user stack pointer
+ p->trapframe->epc = 0; // user program counter
+ p->trapframe->sp = PGSIZE; // user stack pointer
safestrcpy(p->name, "initcode", sizeof(p->name));
p->cwd = namei("/");
@@ -263,10 +263,10 @@ fork(void)
np->parent = p;
// copy saved user registers.
- *(np->tf) = *(p->tf);
+ *(np->trapframe) = *(p->trapframe);
// Cause fork to return 0 in the child.
- np->tf->a0 = 0;
+ np->trapframe->a0 = 0;
// increment reference counts on open file descriptors.
for(i = 0; i < NOFILE; i++)
@@ -457,7 +457,7 @@ scheduler(void)
// before jumping back to us.
p->state = RUNNING;
c->proc = p;
- swtch(&c->scheduler, &p->context);
+ swtch(&c->context, &p->context);
// Process is done running for now.
// It should have changed its p->state before coming back.
@@ -491,7 +491,7 @@ sched(void)
panic("sched interruptible");
intena = mycpu()->intena;
- swtch(&p->context, &mycpu()->scheduler);
+ swtch(&p->context, &mycpu()->context);
mycpu()->intena = intena;
}
diff --git a/kernel/proc.h b/kernel/proc.h
index 538b48a..9c16ea7 100644
--- a/kernel/proc.h
+++ b/kernel/proc.h
@@ -21,7 +21,7 @@ struct context {
// Per-CPU state.
struct cpu {
struct proc *proc; // The process running on this cpu, or null.
- struct context scheduler; // swtch() here to enter scheduler().
+ struct context context; // swtch() here to enter scheduler().
int noff; // Depth of push_off() nesting.
int intena; // Were interrupts enabled before push_off()?
};
@@ -95,10 +95,10 @@ struct proc {
int pid; // Process ID
// these are private to the process, so p->lock need not be held.
- uint64 kstack; // Bottom of kernel stack for this process
+ uint64 kstack; // Virtual address of kernel stack
uint64 sz; // Size of process memory (bytes)
- pagetable_t pagetable; // Page table
- struct trapframe *tf; // data page for trampoline.S
+ pagetable_t pagetable; // User page table
+ struct trapframe *trapframe; // data page for trampoline.S
struct context context; // swtch() here to run process
struct file *ofile[NOFILE]; // Open files
struct inode *cwd; // Current directory
diff --git a/kernel/riscv.h b/kernel/riscv.h
index f46ba59..0aec003 100644
--- a/kernel/riscv.h
+++ b/kernel/riscv.h
@@ -261,7 +261,6 @@ r_time()
static inline void
intr_on()
{
- w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
w_sstatus(r_sstatus() | SSTATUS_SIE);
}
diff --git a/kernel/spinlock.c b/kernel/spinlock.c
index 563532e..9840302 100644
--- a/kernel/spinlock.c
+++ b/kernel/spinlock.c
@@ -34,7 +34,8 @@ acquire(struct spinlock *lk)
// Tell the C compiler and the processor to not move loads or stores
// past this point, to ensure that the critical section's memory
- // references happen after the lock is acquired.
+ // references happen strictly after the lock is acquired.
+ // On RISC-V, this emits a fence instruction.
__sync_synchronize();
// Record info about lock acquisition for holding() and debugging.
@@ -52,8 +53,10 @@ release(struct spinlock *lk)
// Tell the C compiler and the CPU to not move loads or stores
// past this point, to ensure that all the stores in the critical
- // section are visible to other CPUs before the lock is released.
- // On RISC-V, this turns into a fence instruction.
+ // section are visible to other CPUs before the lock is released,
+ // and that loads in the critical section occur strictly before
+ // the lock is released.
+ // On RISC-V, this emits a fence instruction.
__sync_synchronize();
// Release the lock, equivalent to lk->locked = 0.
@@ -69,13 +72,12 @@ release(struct spinlock *lk)
}
// Check whether this cpu is holding the lock.
+// Interrupts must be off.
int
holding(struct spinlock *lk)
{
int r;
- push_off();
r = (lk->locked && lk->cpu == mycpu());
- pop_off();
return r;
}
@@ -100,9 +102,9 @@ pop_off(void)
struct cpu *c = mycpu();
if(intr_get())
panic("pop_off - interruptible");
- c->noff -= 1;
- if(c->noff < 0)
+ if(c->noff < 1)
panic("pop_off");
+ c->noff -= 1;
if(c->noff == 0 && c->intena)
intr_on();
}
diff --git a/kernel/start.c b/kernel/start.c
index 203c5e6..4eb6c2d 100644
--- a/kernel/start.c
+++ b/kernel/start.c
@@ -36,6 +36,7 @@ start()
// delegate all interrupts and exceptions to supervisor mode.
w_medeleg(0xffff);
w_mideleg(0xffff);
+ w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
// ask for clock interrupts.
timerinit();
diff --git a/kernel/syscall.c b/kernel/syscall.c
index 2817e44..c1b3670 100644
--- a/kernel/syscall.c
+++ b/kernel/syscall.c
@@ -37,17 +37,17 @@ argraw(int n)
struct proc *p = myproc();
switch (n) {
case 0:
- return p->tf->a0;
+ return p->trapframe->a0;
case 1:
- return p->tf->a1;
+ return p->trapframe->a1;
case 2:
- return p->tf->a2;
+ return p->trapframe->a2;
case 3:
- return p->tf->a3;
+ return p->trapframe->a3;
case 4:
- return p->tf->a4;
+ return p->trapframe->a4;
case 5:
- return p->tf->a5;
+ return p->trapframe->a5;
}
panic("argraw");
return -1;
@@ -135,12 +135,12 @@ syscall(void)
int num;
struct proc *p = myproc();
- num = p->tf->a7;
+ num = p->trapframe->a7;
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
- p->tf->a0 = syscalls[num]();
+ p->trapframe->a0 = syscalls[num]();
} else {
printf("%d %s: unknown sys call %d\n",
p->pid, p->name, num);
- p->tf->a0 = -1;
+ p->trapframe->a0 = -1;
}
}
diff --git a/kernel/sysfile.c b/kernel/sysfile.c
index 7768d20..015c942 100644
--- a/kernel/sysfile.c
+++ b/kernel/sysfile.c
@@ -341,6 +341,10 @@ sys_open(void)
f->readable = !(omode & O_WRONLY);
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
+ if((omode & O_TRUNC) && ip->type == T_FILE){
+ itrunc(ip);
+ }
+
iunlock(ip);
end_op();
diff --git a/kernel/trampoline.S b/kernel/trampoline.S
index b113bf6..fabaaf9 100644
--- a/kernel/trampoline.S
+++ b/kernel/trampoline.S
@@ -20,7 +20,7 @@ uservec:
# in supervisor mode, but with a
# user page table.
#
- # sscratch points to where the process's p->tf is
+ # sscratch points to where the process's p->trapframe is
# mapped into user space, at TRAPFRAME.
#
@@ -60,20 +60,20 @@ uservec:
sd t5, 272(a0)
sd t6, 280(a0)
- # save the user a0 in p->tf->a0
+ # save the user a0 in p->trapframe->a0
csrr t0, sscratch
sd t0, 112(a0)
- # restore kernel stack pointer from p->tf->kernel_sp
+ # restore kernel stack pointer from p->trapframe->kernel_sp
ld sp, 8(a0)
- # make tp hold the current hartid, from p->tf->kernel_hartid
+ # make tp hold the current hartid, from p->trapframe->kernel_hartid
ld tp, 32(a0)
- # load the address of usertrap(), p->tf->kernel_trap
+ # load the address of usertrap(), p->trapframe->kernel_trap
ld t0, 16(a0)
- # restore kernel page table from p->tf->kernel_satp
+ # restore kernel page table from p->trapframe->kernel_satp
ld t1, 0(a0)
csrw satp, t1
sfence.vma zero, zero
diff --git a/kernel/trap.c b/kernel/trap.c
index ca732f2..a63249e 100644
--- a/kernel/trap.c
+++ b/kernel/trap.c
@@ -48,7 +48,7 @@ usertrap(void)
struct proc *p = myproc();
// save user program counter.
- p->tf->epc = r_sepc();
+ p->trapframe->epc = r_sepc();
if(r_scause() == 8){
// system call
@@ -58,7 +58,7 @@ usertrap(void)
// sepc points to the ecall instruction,
// but we want to return to the next instruction.
- p->tf->epc += 4;
+ p->trapframe->epc += 4;
// an interrupt will change sstatus &c registers,
// so don't enable until done with those registers.
@@ -91,8 +91,9 @@ usertrapret(void)
{
struct proc *p = myproc();
- // turn off interrupts, since we're switching
- // now from kerneltrap() to usertrap().
+ // we're about to switch the destination of traps from
+ // kerneltrap() to usertrap(), so turn off interrupts until
+ // we're back in user space, where usertrap() is correct.
intr_off();
// send syscalls, interrupts, and exceptions to trampoline.S
@@ -100,10 +101,10 @@ usertrapret(void)
// set up trapframe values that uservec will need when
// the process next re-enters the kernel.
- p->tf->kernel_satp = r_satp(); // kernel page table
- p->tf->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
- p->tf->kernel_trap = (uint64)usertrap;
- p->tf->kernel_hartid = r_tp(); // hartid for cpuid()
+ p->trapframe->kernel_satp = r_satp(); // kernel page table
+ p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
+ p->trapframe->kernel_trap = (uint64)usertrap;
+ p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid()
// set up the registers that trampoline.S's sret will use
// to get to user space.
@@ -115,7 +116,7 @@ usertrapret(void)
w_sstatus(x);
// set S Exception Program Counter to the saved user pc.
- w_sepc(p->tf->epc);
+ w_sepc(p->trapframe->epc);
// tell trampoline.S the user page table to switch to.
uint64 satp = MAKE_SATP(p->pagetable);
@@ -129,7 +130,6 @@ usertrapret(void)
// 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()
{
@@ -189,9 +189,16 @@ devintr()
uartintr();
} else if(irq == VIRTIO0_IRQ){
virtio_disk_intr();
+ } else if(irq){
+ printf("unexpected interrupt irq=%d\n", irq);
}
- plic_complete(irq);
+ // the PLIC allows each device to raise at most one
+ // interrupt at a time; tell the PLIC the device is
+ // now allowed to interrupt again.
+ if(irq)
+ plic_complete(irq);
+
return 1;
} else if(scause == 0x8000000000000001L){
// software interrupt from a machine-mode timer interrupt,
diff --git a/kernel/uart.c b/kernel/uart.c
index 3a5cdc4..daf9f04 100644
--- a/kernel/uart.c
+++ b/kernel/uart.c
@@ -18,18 +18,35 @@
// the UART control registers.
// some have different meanings for
// read vs write.
-// http://byterunner.com/16550.html
-#define RHR 0 // receive holding register (for input bytes)
-#define THR 0 // transmit holding register (for output bytes)
-#define IER 1 // interrupt enable register
-#define FCR 2 // FIFO control register
-#define ISR 2 // interrupt status register
-#define LCR 3 // line control register
-#define LSR 5 // line status register
+// see http://byterunner.com/16550.html
+#define RHR 0 // receive holding register (for input bytes)
+#define THR 0 // transmit holding register (for output bytes)
+#define IER 1 // interrupt enable register
+#define IER_TX_ENABLE (1<<0)
+#define IER_RX_ENABLE (1<<1)
+#define FCR 2 // FIFO control register
+#define FCR_FIFO_ENABLE (1<<0)
+#define FCR_FIFO_CLEAR (3<<1) // clear the content of the two FIFOs
+#define ISR 2 // interrupt status register
+#define LCR 3 // line control register
+#define LCR_EIGHT_BITS (3<<0)
+#define LCR_BAUD_LATCH (1<<7) // special mode to set baud rate
+#define LSR 5 // line status register
+#define LSR_RX_READY (1<<0) // input is waiting to be read from RHR
+#define LSR_TX_IDLE (1<<5) // THR can accept another character to send
#define ReadReg(reg) (*(Reg(reg)))
#define WriteReg(reg, v) (*(Reg(reg)) = (v))
+// the transmit output buffer.
+struct spinlock uart_tx_lock;
+#define UART_TX_BUF_SIZE 32
+char uart_tx_buf[UART_TX_BUF_SIZE];
+int uart_tx_w; // write next to uart_tx_buf[uart_tx_w++]
+int uart_tx_r; // read next from uart_tx_buf[uar_tx_r++]
+
+void uartstart();
+
void
uartinit(void)
{
@@ -37,7 +54,7 @@ uartinit(void)
WriteReg(IER, 0x00);
// special mode to set baud rate.
- WriteReg(LCR, 0x80);
+ WriteReg(LCR, LCR_BAUD_LATCH);
// LSB for baud rate of 38.4K.
WriteReg(0, 0x03);
@@ -47,23 +64,87 @@ uartinit(void)
// leave set-baud mode,
// and set word length to 8 bits, no parity.
- WriteReg(LCR, 0x03);
+ WriteReg(LCR, LCR_EIGHT_BITS);
// reset and enable FIFOs.
- WriteReg(FCR, 0x07);
+ WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
+
+ // enable transmit and receive interrupts.
+ WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);
- // enable receive interrupts.
- WriteReg(IER, 0x01);
+ initlock(&uart_tx_lock, "uart");
}
-// write one output character to the UART.
+// add a character to the output buffer and tell the
+// UART to start sending if it isn't already.
+// blocks if the output buffer is full.
+// because it may block, it can't be called
+// from interrupts; it's only suitable for use
+// by write().
void
uartputc(int c)
{
+ acquire(&uart_tx_lock);
+ while(1){
+ if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){
+ // buffer is full.
+ // wait for uartstart() to open up space in the buffer.
+ sleep(&uart_tx_r, &uart_tx_lock);
+ } else {
+ uart_tx_buf[uart_tx_w] = c;
+ uart_tx_w = (uart_tx_w + 1) % UART_TX_BUF_SIZE;
+ uartstart();
+ release(&uart_tx_lock);
+ return;
+ }
+ }
+}
+
+// alternate version of uartputc() that doesn't
+// use interrupts, for use by kernel printf() and
+// to echo characters. it spins waiting for the uart's
+// output register to be empty.
+void
+uartputc_sync(int c)
+{
+ push_off();
+
// wait for Transmit Holding Empty to be set in LSR.
- while((ReadReg(LSR) & (1 << 5)) == 0)
+ while((ReadReg(LSR) & LSR_TX_IDLE) == 0)
;
WriteReg(THR, c);
+
+ pop_off();
+}
+
+// if the UART is idle, and a character is waiting
+// in the transmit buffer, send it.
+// caller must hold uart_tx_lock.
+// called from both the top- and bottom-half.
+void
+uartstart()
+{
+ while(1){
+ if(uart_tx_w == uart_tx_r){
+ // transmit buffer is empty.
+ return;
+ }
+
+ if((ReadReg(LSR) & LSR_TX_IDLE) == 0){
+ // the UART transmit holding register is full,
+ // so we cannot give it another byte.
+ // it will interrupt when it's ready for a new byte.
+ return;
+ }
+
+ int c = uart_tx_buf[uart_tx_r];
+ uart_tx_r = (uart_tx_r + 1) % UART_TX_BUF_SIZE;
+
+ // maybe uartputc() is waiting for space in the buffer.
+ wakeup(&uart_tx_r);
+
+ WriteReg(THR, c);
+ }
}
// read one input character from the UART.
@@ -79,14 +160,22 @@ uartgetc(void)
}
}
-// trap.c calls here when the uart interrupts.
+// handle a uart interrupt, raised because input has
+// arrived, or the uart is ready for more output, or
+// both. called from trap.c.
void
uartintr(void)
{
+ // read and process incoming characters.
while(1){
int c = uartgetc();
if(c == -1)
break;
consoleintr(c);
}
+
+ // send buffered characters.
+ acquire(&uart_tx_lock);
+ uartstart();
+ release(&uart_tx_lock);
}
diff --git a/kernel/vm.c b/kernel/vm.c
index 5e0b2aa..636f11a 100644
--- a/kernel/vm.c
+++ b/kernel/vm.c
@@ -16,9 +16,7 @@ extern char etext[]; // kernel.ld sets this to end of kernel code.
extern char trampoline[]; // trampoline.S
/*
- * create a direct-map page table for the kernel and
- * turn on paging. called early, in supervisor mode.
- * the page allocator is already initialized.
+ * create a direct-map page table for the kernel.
*/
void
kvminit()
@@ -70,7 +68,7 @@ kvminithart()
// 21..29 -- 9 bits of level-1 index.
// 12..20 -- 9 bits of level-0 index.
// 0..11 -- 12 bits of byte offset within the page.
-static pte_t *
+pte_t *
walk(pagetable_t pagetable, uint64 va, int alloc)
{
if(va >= MAXVA)
@@ -278,7 +276,7 @@ uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
// Recursively free page-table pages.
// All leaf mappings must already have been removed.
-static void
+void
freewalk(pagetable_t pagetable)
{
// there are 2^9 = 512 PTEs in a page table.
@@ -301,7 +299,8 @@ freewalk(pagetable_t pagetable)
void
uvmfree(pagetable_t pagetable, uint64 sz)
{
- uvmunmap(pagetable, 0, sz, 1);
+ if(sz > 0)
+ uvmunmap(pagetable, 0, sz, 1);
freewalk(pagetable);
}
diff --git a/user/alarmtest.c b/user/alarmtest.c
deleted file mode 100644
index ca3db23..0000000
--- a/user/alarmtest.c
+++ /dev/null
@@ -1,88 +0,0 @@
-//
-// test program for the alarm lab.
-// you can modify this file for testing,
-// but please make sure your kernel
-// modifications pass the original
-// versions of these tests.
-//
-
-#include "kernel/param.h"
-#include "kernel/types.h"
-#include "kernel/stat.h"
-#include "kernel/riscv.h"
-#include "user/user.h"
-
-void test0();
-void test1();
-void periodic();
-
-int
-main(int argc, char *argv[])
-{
- test0();
- test1();
- exit();
-}
-
-volatile static int count;
-
-void
-periodic()
-{
- count = count + 1;
- printf("alarm!\n");
- sigreturn();
-}
-
-// tests whether the kernel calls
-// the alarm handler even a single time.
-void
-test0()
-{
- int i;
- printf("test0 start\n");
- count = 0;
- sigalarm(2, periodic);
- for(i = 0; i < 1000*500000; i++){
- if((i % 250000) == 0)
- write(2, ".", 1);
- if(count > 0)
- break;
- }
- sigalarm(0, 0);
- if(count > 0){
- printf("test0 passed\n");
- } else {
- printf("test0 failed\n");
- }
-}
-
-void __attribute__ ((noinline)) foo(int i, int *j) {
- if((i % 2500000) == 0) {
- write(2, ".", 1);
- }
- *j += 1;
-}
-
-void
-test1()
-{
- int i;
- int j;
-
- printf("test1 start\n");
- count = 0;
- j = 0;
- sigalarm(2, periodic);
- for(i = 0; i < 500000000; i++){
- if(count >= 10)
- break;
- foo(i, &j);
- }
- if(i != j || count < 10){
- // i should equal j
- printf("test1 failed\n");
- } else {
- printf("test1 passed\n");
- }
-}
diff --git a/user/cat.c b/user/cat.c
index 36939d8..598f005 100644
--- a/user/cat.c
+++ b/user/cat.c
@@ -11,12 +11,12 @@ cat(int fd)
while((n = read(fd, buf, sizeof(buf))) > 0) {
if (write(1, buf, n) != n) {
- printf("cat: write error\n");
+ fprintf(2, "cat: write error\n");
exit(1);
}
}
if(n < 0){
- printf("cat: read error\n");
+ fprintf(2, "cat: read error\n");
exit(1);
}
}
@@ -28,12 +28,12 @@ main(int argc, char *argv[])
if(argc <= 1){
cat(0);
- exit(1);
+ exit(0);
}
for(i = 1; i < argc; i++){
if((fd = open(argv[i], 0)) < 0){
- printf("cat: cannot open %s\n", argv[i]);
+ fprintf(2, "cat: cannot open %s\n", argv[i]);
exit(1);
}
cat(fd);
diff --git a/user/grind.c b/user/grind.c
new file mode 100644
index 0000000..14e2aae
--- /dev/null
+++ b/user/grind.c
@@ -0,0 +1,333 @@
+//
+// run random system calls in parallel forever.
+//
+
+#include "kernel/param.h"
+#include "kernel/types.h"
+#include "kernel/stat.h"
+#include "user/user.h"
+#include "kernel/fs.h"
+#include "kernel/fcntl.h"
+#include "kernel/syscall.h"
+#include "kernel/memlayout.h"
+#include "kernel/riscv.h"
+
+// from FreeBSD.
+int
+do_rand(unsigned long *ctx)
+{
+/*
+ * Compute x = (7^5 * x) mod (2^31 - 1)
+ * without overflowing 31 bits:
+ * (2^31 - 1) = 127773 * (7^5) + 2836
+ * From "Random number generators: good ones are hard to find",
+ * Park and Miller, Communications of the ACM, vol. 31, no. 10,
+ * October 1988, p. 1195.
+ */
+ long hi, lo, x;
+
+ /* Transform to [1, 0x7ffffffe] range. */
+ x = (*ctx % 0x7ffffffe) + 1;
+ hi = x / 127773;
+ lo = x % 127773;
+ x = 16807 * lo - 2836 * hi;
+ if (x < 0)
+ x += 0x7fffffff;
+ /* Transform to [0, 0x7ffffffd] range. */
+ x--;
+ *ctx = x;
+ return (x);
+}
+
+unsigned long rand_next = 1;
+
+int
+rand(void)
+{
+ return (do_rand(&rand_next));
+}
+
+void
+go(int which_child)
+{
+ int fd = -1;
+ static char buf[999];
+ char *break0 = sbrk(0);
+ uint64 iters = 0;
+
+ mkdir("grindir");
+ if(chdir("grindir") != 0){
+ printf("chdir grindir failed\n");
+ exit(1);
+ }
+ chdir("/");
+
+ while(1){
+ iters++;
+ if((iters % 500) == 0)
+ write(1, which_child?"B":"A", 1);
+ int what = rand() % 23;
+ if(what == 1){
+ close(open("grindir/../a", O_CREATE|O_RDWR));
+ } else if(what == 2){
+ close(open("grindir/../grindir/../b", O_CREATE|O_RDWR));
+ } else if(what == 3){
+ unlink("grindir/../a");
+ } else if(what == 4){
+ if(chdir("grindir") != 0){
+ printf("chdir grindir failed\n");
+ exit(1);
+ }
+ unlink("../b");
+ chdir("/");
+ } else if(what == 5){
+ close(fd);
+ fd = open("/grindir/../a", O_CREATE|O_RDWR);
+ } else if(what == 6){
+ close(fd);
+ fd = open("/./grindir/./../b", O_CREATE|O_RDWR);
+ } else if(what == 7){
+ write(fd, buf, sizeof(buf));
+ } else if(what == 8){
+ read(fd, buf, sizeof(buf));
+ } else if(what == 9){
+ mkdir("grindir/../a");
+ close(open("a/../a/./a", O_CREATE|O_RDWR));
+ unlink("a/a");
+ } else if(what == 10){
+ mkdir("/../b");
+ close(open("grindir/../b/b", O_CREATE|O_RDWR));
+ unlink("b/b");
+ } else if(what == 11){
+ unlink("b");
+ link("../grindir/./../a", "../b");
+ } else if(what == 12){
+ unlink("../grindir/../a");
+ link(".././b", "/grindir/../a");
+ } else if(what == 13){
+ int pid = fork();
+ if(pid == 0){
+ exit(0);
+ } else if(pid < 0){
+ printf("grind: fork failed\n");
+ exit(1);
+ }
+ wait(0);
+ } else if(what == 14){
+ int pid = fork();
+ if(pid == 0){
+ fork();
+ fork();
+ exit(0);
+ } else if(pid < 0){
+ printf("grind: fork failed\n");
+ exit(1);
+ }
+ wait(0);
+ } else if(what == 15){
+ sbrk(6011);
+ } else if(what == 16){
+ if(sbrk(0) > break0)
+ sbrk(-(sbrk(0) - break0));
+ } else if(what == 17){
+ int pid = fork();
+ if(pid == 0){
+ close(open("a", O_CREATE|O_RDWR));
+ exit(0);
+ } else if(pid < 0){
+ printf("grind: fork failed\n");
+ exit(1);
+ }
+ if(chdir("../grindir/..") != 0){
+ printf("chdir failed\n");
+ exit(1);
+ }
+ kill(pid);
+ wait(0);
+ } else if(what == 18){
+ int pid = fork();
+ if(pid == 0){
+ kill(getpid());
+ exit(0);
+ } else if(pid < 0){
+ printf("grind: fork failed\n");
+ exit(1);
+ }
+ wait(0);
+ } else if(what == 19){
+ int fds[2];
+ if(pipe(fds) < 0){
+ printf("grind: pipe failed\n");
+ exit(1);
+ }
+ int pid = fork();
+ if(pid == 0){
+ fork();
+ fork();
+ if(write(fds[1], "x", 1) != 1)
+ printf("grind: pipe write failed\n");
+ char c;
+ if(read(fds[0], &c, 1) != 1)
+ printf("grind: pipe read failed\n");
+ exit(0);
+ } else if(pid < 0){
+ printf("grind: fork failed\n");
+ exit(1);
+ }
+ close(fds[0]);
+ close(fds[1]);
+ wait(0);
+ } else if(what == 20){
+ int pid = fork();
+ if(pid == 0){
+ unlink("a");
+ mkdir("a");
+ chdir("a");
+ unlink("../a");
+ fd = open("x", O_CREATE|O_RDWR);
+ unlink("x");
+ exit(0);
+ } else if(pid < 0){
+ printf("fork failed\n");
+ exit(1);
+ }
+ wait(0);
+ } else if(what == 21){
+ unlink("c");
+ // should always succeed. check that there are free i-nodes,
+ // file descriptors, blocks.
+ int fd1 = open("c", O_CREATE|O_RDWR);
+ if(fd1 < 0){
+ printf("create c failed\n");
+ exit(1);
+ }
+ if(write(fd1, "x", 1) != 1){
+ printf("write c failed\n");
+ exit(1);
+ }
+ struct stat st;
+ if(fstat(fd1, &st) != 0){
+ printf("fstat failed\n");
+ exit(1);
+ }
+ if(st.size != 1){
+ printf("fstat reports wrong size %d\n", (int)st.size);
+ exit(1);
+ }
+ if(st.ino > 200){
+ printf("fstat reports crazy i-number %d\n", st.ino);
+ exit(1);
+ }
+ close(fd1);
+ unlink("c");
+ } else if(what == 22){
+ // echo hi | cat
+ int aa[2], bb[2];
+ if(pipe(aa) < 0){
+ fprintf(2, "pipe failed\n");
+ exit(1);
+ }
+ if(pipe(bb) < 0){
+ fprintf(2, "pipe failed\n");
+ exit(1);
+ }
+ int pid1 = fork();
+ if(pid1 == 0){
+ close(bb[0]);
+ close(bb[1]);
+ close(aa[0]);
+ close(1);
+ if(dup(aa[1]) != 1){
+ fprintf(2, "dup failed\n");
+ exit(1);
+ }
+ close(aa[1]);
+ char *args[3] = { "echo", "hi", 0 };
+ exec("grindir/../echo", args);
+ fprintf(2, "echo: not found\n");
+ exit(2);
+ } else if(pid1 < 0){
+ fprintf(2, "fork failed\n");
+ exit(3);
+ }
+ int pid2 = fork();
+ if(pid2 == 0){
+ close(aa[1]);
+ close(bb[0]);
+ close(0);
+ if(dup(aa[0]) != 0){
+ fprintf(2, "dup failed\n");
+ exit(4);
+ }
+ close(aa[0]);
+ close(1);
+ if(dup(bb[1]) != 1){
+ fprintf(2, "dup failed\n");
+ exit(5);
+ }
+ close(bb[1]);
+ char *args[2] = { "cat", 0 };
+ exec("/cat", args);
+ fprintf(2, "cat: not found\n");
+ exit(6);
+ } else if(pid2 < 0){
+ fprintf(2, "fork failed\n");
+ exit(7);
+ }
+ close(aa[0]);
+ close(aa[1]);
+ close(bb[1]);
+ char buf[3] = { 0, 0, 0 };
+ read(bb[0], buf+0, 1);
+ read(bb[0], buf+1, 1);
+ close(bb[0]);
+ int st1, st2;
+ wait(&st1);
+ wait(&st2);
+ if(st1 != 0 || st2 != 0 || strcmp(buf, "hi") != 0){
+ printf("exec pipeline failed %d %d \"%s\"\n", st1, st2, buf);
+ exit(1);
+ }
+ }
+ }
+}
+
+int
+main()
+{
+ unlink("a");
+ unlink("b");
+
+ int pid1 = fork();
+ if(pid1 < 0){
+ printf("grind: fork failed\n");
+ exit(1);
+ }
+ if(pid1 == 0){
+ rand_next = 31;
+ go(0);
+ exit(0);
+ }
+
+ int pid2 = fork();
+ if(pid2 < 0){
+ printf("grind: fork failed\n");
+ exit(1);
+ }
+ if(pid2 == 0){
+ rand_next = 7177;
+ go(1);
+ exit(0);
+ }
+
+ int st1 = -1;
+ wait(&st1);
+ if(st1 != 0){
+ kill(pid1);
+ kill(pid2);
+ }
+ int st2 = -1;
+ wait(&st2);
+
+ exit(0);
+}
diff --git a/user/sh.c b/user/sh.c
index a593bc0..83dd513 100644
--- a/user/sh.c
+++ b/user/sh.c
@@ -386,7 +386,7 @@ parseredirs(struct cmd *cmd, char **ps, char *es)
cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
break;
case '>':
- cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
+ cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE|O_TRUNC, 1);
break;
case '+': // >>
cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
diff --git a/user/ulib.c b/user/ulib.c
index ddda0f5..4775939 100644
--- a/user/ulib.c
+++ b/user/ulib.c
@@ -103,7 +103,34 @@ memmove(void *vdst, const void *vsrc, int n)
dst = vdst;
src = vsrc;
- while(n-- > 0)
- *dst++ = *src++;
+ if (src > dst) {
+ while(n-- > 0)
+ *dst++ = *src++;
+ } else {
+ dst += n;
+ src += n;
+ while(n-- > 0)
+ *--dst = *--src;
+ }
return vdst;
}
+
+int
+memcmp(const void *s1, const void *s2, uint n)
+{
+ const char *p1 = s1, *p2 = s2;
+ while (n-- > 0) {
+ if (*p1 != *p2) {
+ return *p1 - *p2;
+ }
+ p1++;
+ p2++;
+ }
+ return 0;
+}
+
+void *
+memcpy(void *dst, const void *src, uint n)
+{
+ return memmove(dst, src, n);
+}
diff --git a/user/user.h b/user/user.h
index 03af731..b71ecda 100644
--- a/user/user.h
+++ b/user/user.h
@@ -38,3 +38,5 @@ void* memset(void*, int, uint);
void* malloc(uint);
void free(void*);
int atoi(const char*);
+int memcmp(const void *, const void *, uint);
+void *memcpy(void *, const void *, uint);
diff --git a/user/usertests.c b/user/usertests.c
index db9f680..dfe0039 100644
--- a/user/usertests.c
+++ b/user/usertests.c
@@ -22,6 +22,352 @@
char buf[BUFSZ];
char name[3];
+// what if you pass ridiculous pointers to system calls
+// that read user memory with copyin?
+void
+copyin(char *s)
+{
+ uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff };
+
+ for(int ai = 0; ai < 2; ai++){
+ uint64 addr = addrs[ai];
+
+ int fd = open("copyin1", O_CREATE|O_WRONLY);
+ if(fd < 0){
+ printf("open(copyin1) failed\n");
+ exit(1);
+ }
+ int n = write(fd, (void*)addr, 8192);
+ if(n >= 0){
+ printf("write(fd, %p, 8192) returned %d, not -1\n", addr, n);
+ exit(1);
+ }
+ close(fd);
+ unlink("copyin1");
+
+ n = write(1, (char*)addr, 8192);
+ if(n > 0){
+ printf("write(1, %p, 8192) returned %d, not -1 or 0\n", addr, n);
+ exit(1);
+ }
+
+ int fds[2];
+ if(pipe(fds) < 0){
+ printf("pipe() failed\n");
+ exit(1);
+ }
+ n = write(fds[1], (char*)addr, 8192);
+ if(n > 0){
+ printf("write(pipe, %p, 8192) returned %d, not -1 or 0\n", addr, n);
+ exit(1);
+ }
+ close(fds[0]);
+ close(fds[1]);
+ }
+}
+
+// what if you pass ridiculous pointers to system calls
+// that write user memory with copyout?
+void
+copyout(char *s)
+{
+ uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff };
+
+ for(int ai = 0; ai < 2; ai++){
+ uint64 addr = addrs[ai];
+
+ int fd = open("README", 0);
+ if(fd < 0){
+ printf("open(README) failed\n");
+ exit(1);
+ }
+ int n = read(fd, (void*)addr, 8192);
+ if(n > 0){
+ printf("read(fd, %p, 8192) returned %d, not -1 or 0\n", addr, n);
+ exit(1);
+ }
+ close(fd);
+
+ int fds[2];
+ if(pipe(fds) < 0){
+ printf("pipe() failed\n");
+ exit(1);
+ }
+ n = write(fds[1], "x", 1);
+ if(n != 1){
+ printf("pipe write failed\n");
+ exit(1);
+ }
+ n = read(fds[0], (void*)addr, 8192);
+ if(n > 0){
+ printf("read(pipe, %p, 8192) returned %d, not -1 or 0\n", addr, n);
+ exit(1);
+ }
+ close(fds[0]);
+ close(fds[1]);
+ }
+}
+
+// what if you pass ridiculous string pointers to system calls?
+void
+copyinstr1(char *s)
+{
+ uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff };
+
+ for(int ai = 0; ai < 2; ai++){
+ uint64 addr = addrs[ai];
+
+ int fd = open((char *)addr, O_CREATE|O_WRONLY);
+ if(fd >= 0){
+ printf("open(%p) returned %d, not -1\n", addr, fd);
+ exit(1);
+ }
+ }
+}
+
+// what if a string system call argument is exactly the size
+// of the kernel buffer it is copied into, so that the null
+// would fall just beyond the end of the kernel buffer?
+void
+copyinstr2(char *s)
+{
+ char b[MAXPATH+1];
+
+ for(int i = 0; i < MAXPATH; i++)
+ b[i] = 'x';
+ b[MAXPATH] = '\0';
+
+ int ret = unlink(b);
+ if(ret != -1){
+ printf("unlink(%s) returned %d, not -1\n", b, ret);
+ exit(1);
+ }
+
+ int fd = open(b, O_CREATE | O_WRONLY);
+ if(fd != -1){
+ printf("open(%s) returned %d, not -1\n", b, fd);
+ exit(1);
+ }
+
+ ret = link(b, b);
+ if(ret != -1){
+ printf("link(%s, %s) returned %d, not -1\n", b, b, ret);
+ exit(1);
+ }
+
+ char *args[] = { "xx", 0 };
+ ret = exec(b, args);
+ if(ret != -1){
+ printf("exec(%s) returned %d, not -1\n", b, fd);
+ exit(1);
+ }
+
+ int pid = fork();
+ if(pid < 0){
+ printf("fork failed\n");
+ exit(1);
+ }
+ if(pid == 0){
+ static char big[PGSIZE+1];
+ for(int i = 0; i < PGSIZE; i++)
+ big[i] = 'x';
+ big[PGSIZE] = '\0';
+ char *args2[] = { big, big, big, 0 };
+ ret = exec("echo", args2);
+ if(ret != -1){
+ printf("exec(echo, BIG) returned %d, not -1\n", fd);
+ exit(1);
+ }
+ exit(747); // OK
+ }
+
+ int st = 0;
+ wait(&st);
+ if(st != 747){
+ printf("exec(echo, BIG) succeeded, should have failed\n");
+ exit(1);
+ }
+}
+
+// what if a string argument crosses over the end of last user page?
+void
+copyinstr3(char *s)
+{
+ sbrk(8192);
+ uint64 top = (uint64) sbrk(0);
+ if((top % PGSIZE) != 0){
+ sbrk(PGSIZE - (top % PGSIZE));
+ }
+ top = (uint64) sbrk(0);
+ if(top % PGSIZE){
+ printf("oops\n");
+ exit(1);
+ }
+
+ char *b = (char *) (top - 1);
+ *b = 'x';
+
+ int ret = unlink(b);
+ if(ret != -1){
+ printf("unlink(%s) returned %d, not -1\n", b, ret);
+ exit(1);
+ }
+
+ int fd = open(b, O_CREATE | O_WRONLY);
+ if(fd != -1){
+ printf("open(%s) returned %d, not -1\n", b, fd);
+ exit(1);
+ }
+
+ ret = link(b, b);
+ if(ret != -1){
+ printf("link(%s, %s) returned %d, not -1\n", b, b, ret);
+ exit(1);
+ }
+
+ char *args[] = { "xx", 0 };
+ ret = exec(b, args);
+ if(ret != -1){
+ printf("exec(%s) returned %d, not -1\n", b, fd);
+ exit(1);
+ }
+}
+
+// test O_TRUNC.
+void
+truncate1(char *s)
+{
+ char buf[32];
+
+ unlink("truncfile");
+ int fd1 = open("truncfile", O_CREATE|O_WRONLY|O_TRUNC);
+ write(fd1, "abcd", 4);
+ close(fd1);
+
+ int fd2 = open("truncfile", O_RDONLY);
+ int n = read(fd2, buf, sizeof(buf));
+ if(n != 4){
+ printf("%s: read %d bytes, wanted 4\n", s, n);
+ exit(1);
+ }
+
+ fd1 = open("truncfile", O_WRONLY|O_TRUNC);
+
+ int fd3 = open("truncfile", O_RDONLY);
+ n = read(fd3, buf, sizeof(buf));
+ if(n != 0){
+ printf("aaa fd3=%d\n", fd3);
+ printf("%s: read %d bytes, wanted 0\n", s, n);
+ exit(1);
+ }
+
+ n = read(fd2, buf, sizeof(buf));
+ if(n != 0){
+ printf("bbb fd2=%d\n", fd2);
+ printf("%s: read %d bytes, wanted 0\n", s, n);
+ exit(1);
+ }
+
+ write(fd1, "abcdef", 6);
+
+ n = read(fd3, buf, sizeof(buf));
+ if(n != 6){
+ printf("%s: read %d bytes, wanted 6\n", s, n);
+ exit(1);
+ }
+
+ n = read(fd2, buf, sizeof(buf));
+ if(n != 2){
+ printf("%s: read %d bytes, wanted 2\n", s, n);
+ exit(1);
+ }
+
+ unlink("truncfile");
+
+ close(fd1);
+ close(fd2);
+ close(fd3);
+}
+
+// write to an open FD whose file has just been truncated.
+// this causes a write at an offset beyond the end of the file.
+// such writes fail on xv6 (unlike POSIX) but at least
+// they don't crash.
+void
+truncate2(char *s)
+{
+ unlink("truncfile");
+
+ int fd1 = open("truncfile", O_CREATE|O_TRUNC|O_WRONLY);
+ write(fd1, "abcd", 4);
+
+ int fd2 = open("truncfile", O_TRUNC|O_WRONLY);
+
+ int n = write(fd1, "x", 1);
+ if(n != -1){
+ printf("%s: write returned %d, expected -1\n", s, n);
+ exit(1);
+ }
+
+ unlink("truncfile");
+ close(fd1);
+ close(fd2);
+}
+
+void
+truncate3(char *s)
+{
+ int pid, xstatus;
+
+ close(open("truncfile", O_CREATE|O_TRUNC|O_WRONLY));
+
+ pid = fork();
+ if(pid < 0){
+ printf("%s: fork failed\n", s);
+ exit(1);
+ }
+
+ if(pid == 0){
+ for(int i = 0; i < 100; i++){
+ char buf[32];
+ int fd = open("truncfile", O_WRONLY);
+ if(fd < 0){
+ printf("%s: open failed\n", s);
+ exit(1);
+ }
+ int n = write(fd, "1234567890", 10);
+ if(n != 10){
+ printf("%s: write got %d, expected 10\n", s, n);
+ exit(1);
+ }
+ close(fd);
+ fd = open("truncfile", O_RDONLY);
+ read(fd, buf, sizeof(buf));
+ close(fd);
+ }
+ exit(0);
+ }
+
+ for(int i = 0; i < 150; i++){
+ int fd = open("truncfile", O_CREATE|O_WRONLY|O_TRUNC);
+ if(fd < 0){
+ printf("%s: open failed\n", s);
+ exit(1);
+ }
+ int n = write(fd, "xxx", 3);
+ if(n != 3){
+ printf("%s: write got %d, expected 3\n", s, n);
+ exit(1);
+ }
+ close(fd);
+ }
+
+ wait(&xstatus);
+ unlink("truncfile");
+ exit(xstatus);
+}
+
+
// does chdir() call iput(p->cwd) in a transaction?
void
iputtest(char *s)
@@ -1038,11 +1384,15 @@ concreate(char *s)
close(open(file, 0));
close(open(file, 0));
close(open(file, 0));
+ close(open(file, 0));
+ close(open(file, 0));
} else {
unlink(file);
unlink(file);
unlink(file);
unlink(file);
+ unlink(file);
+ unlink(file);
}
if(pid == 0)
exit(0);
@@ -1106,7 +1456,7 @@ bigdir(char *s)
name[2] = '0' + (i % 64);
name[3] = '\0';
if(link("bd", name) != 0){
- printf("%s: bigdir link failed\n", s);
+ printf("%s: bigdir link(bd, %s) failed\n", s, name);
exit(1);
}
}
@@ -1335,8 +1685,8 @@ bigfile(char *s)
enum { N = 20, SZ=600 };
int fd, i, total, cc;
- unlink("bigfile");
- fd = open("bigfile", O_CREATE | O_RDWR);
+ unlink("bigfile.dat");
+ fd = open("bigfile.dat", O_CREATE | O_RDWR);
if(fd < 0){
printf("%s: cannot create bigfile", s);
exit(1);
@@ -1350,7 +1700,7 @@ bigfile(char *s)
}
close(fd);
- fd = open("bigfile", 0);
+ fd = open("bigfile.dat", 0);
if(fd < 0){
printf("%s: cannot open bigfile\n", s);
exit(1);
@@ -1379,7 +1729,7 @@ bigfile(char *s)
printf("%s: read bigfile wrong total\n", s);
exit(1);
}
- unlink("bigfile");
+ unlink("bigfile.dat");
}
void
@@ -1418,6 +1768,14 @@ fourteen(char *s)
printf("%s: mkdir 12345678901234/123456789012345 succeeded!\n", s);
exit(1);
}
+
+ // clean up
+ unlink("123456789012345/12345678901234");
+ unlink("12345678901234/12345678901234");
+ unlink("12345678901234/12345678901234/12345678901234");
+ unlink("123456789012345/123456789012345/123456789012345");
+ unlink("12345678901234/123456789012345");
+ unlink("12345678901234");
}
void
@@ -1512,7 +1870,8 @@ dirfile(char *s)
close(fd);
}
-// test that iput() is called at the end of _namei()
+// test that iput() is called at the end of _namei().
+// also tests empty file names.
void
iref(char *s)
{
@@ -1539,6 +1898,12 @@ iref(char *s)
unlink("xx");
}
+ // clean up
+ for(i = 0; i < NINODE + 1; i++){
+ chdir("..");
+ unlink("irefd");
+ }
+
chdir("/");
}
@@ -2087,13 +2452,35 @@ badarg(char *s)
exit(0);
}
+//
+// use sbrk() to count how many free physical memory pages there are.
+//
+int
+countfree()
+{
+ uint64 sz0 = (uint64)sbrk(0);
+ int n = 0;
+
+ while(1){
+ uint64 a = (uint64) sbrk(4096);
+ if(a == 0xffffffffffffffff){
+ break;
+ }
+ // modify the memory to make sure it's really allocated.
+ *(char *)(a - 1) = 1;
+ n += 1;
+ }
+ sbrk(-((uint64)sbrk(0) - sz0));
+ return n;
+}
+
// run each test in its own process. run returns 1 if child's exit()
// indicates success.
int
run(void f(char *), char *s) {
int pid;
int xstatus;
-
+
printf("test %s: ", s);
if((pid = fork()) < 0) {
printf("runtest: fork error\n");
@@ -2105,9 +2492,9 @@ run(void f(char *), char *s) {
} else {
wait(&xstatus);
if(xstatus != 0)
- printf("FAILED\n", s);
+ printf("FAILED\n");
else
- printf("OK\n", s);
+ printf("OK\n");
return xstatus == 0;
}
}
@@ -2115,15 +2502,30 @@ run(void f(char *), char *s) {
int
main(int argc, char *argv[])
{
- char *n = 0;
- if(argc > 1) {
- n = argv[1];
+ int continuous = 0;
+ char *justone = 0;
+
+ if(argc == 2 && strcmp(argv[1], "-c") == 0){
+ continuous = 1;
+ } else if(argc == 2 && argv[1][0] != '-'){
+ justone = argv[1];
+ } else if(argc > 1){
+ printf("Usage: usertests [-c] [testname]\n");
+ exit(1);
}
struct test {
void (*f)(char *);
char *s;
} tests[] = {
+ {copyin, "copyin"},
+ {copyout, "copyout"},
+ {copyinstr1, "copyinstr1"},
+ {copyinstr2, "copyinstr2"},
+ {copyinstr3, "copyinstr3"},
+ {truncate1, "truncate1"},
+ {truncate2, "truncate2"},
+ {truncate3, "truncate3"},
{reparent2, "reparent2"},
{pgbug, "pgbug" },
{sbrkbugs, "sbrkbugs" },
@@ -2173,25 +2575,48 @@ main(int argc, char *argv[])
{bigdir, "bigdir"}, // slow
{ 0, 0},
};
-
- printf("usertests starting\n");
- if(open("usertests.ran", 0) >= 0){
- printf("already ran user tests -- rebuild fs.img (rm fs.img; make fs.img)\n");
- exit(1);
+ if(continuous){
+ printf("continuous usertests starting\n");
+ while(1){
+ int fail = 0;
+ int free0 = countfree();
+ for (struct test *t = tests; t->s != 0; t++) {
+ if(!run(t->f, t->s)){
+ fail = 1;
+ break;
+ }
+ }
+ if(fail){
+ printf("SOME TESTS FAILED\n");
+ exit(1);
+ }
+ int free1 = countfree();
+ if(free1 < free0){
+ printf("FAILED -- lost some free pages\n");
+ exit(1);
+ }
+ }
}
- close(open("usertests.ran", O_CREATE));
+ printf("usertests starting\n");
+ int free0 = countfree();
int fail = 0;
for (struct test *t = tests; t->s != 0; t++) {
- if((n == 0) || strcmp(t->s, n) == 0) {
+ if((justone == 0) || strcmp(t->s, justone) == 0) {
if(!run(t->f, t->s))
fail = 1;
}
}
- if(!fail)
- printf("ALL TESTS PASSED\n");
- else
+
+ if(fail){
printf("SOME TESTS FAILED\n");
- exit(1); // not reached.
+ exit(1);
+ } else if(countfree() < free0){
+ printf("FAILED -- lost some free pages\n");
+ exit(1);
+ } else {
+ printf("ALL TESTS PASSED\n");
+ exit(0);
+ }
}