Merge branch 'riscv' into riscv
This commit is contained in:
commit
c31d35d803
31 changed files with 1074 additions and 251 deletions
22
.editorconfig
Normal file
22
.editorconfig
Normal file
|
@ -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
|
13
Makefile
13
Makefile
|
@ -40,8 +40,10 @@ TOOLPREFIX := $(shell if riscv64-unknown-elf-objdump -i 2>&1 | grep 'elf64-big'
|
||||||
then echo 'riscv64-unknown-elf-'; \
|
then echo 'riscv64-unknown-elf-'; \
|
||||||
elif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
|
elif riscv64-linux-gnu-objdump -i 2>&1 | grep 'elf64-big' >/dev/null 2>&1; \
|
||||||
then echo 'riscv64-linux-gnu-'; \
|
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; \
|
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 "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \
|
||||||
echo "***" 1>&2; exit 1; fi)
|
echo "***" 1>&2; exit 1; fi)
|
||||||
endif
|
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
|
$(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
|
$(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
|
gcc -Werror -Wall -I. -o mkfs/mkfs mkfs/mkfs.c
|
||||||
|
|
||||||
# Prevent deletion of intermediate files, e.g. cat.o, after first build, so
|
# Prevent deletion of intermediate files, e.g. cat.o, after first build, so
|
||||||
|
@ -127,6 +129,7 @@ UPROGS=\
|
||||||
$U/_sh\
|
$U/_sh\
|
||||||
$U/_stressfs\
|
$U/_stressfs\
|
||||||
$U/_usertests\
|
$U/_usertests\
|
||||||
|
$U/_grind\
|
||||||
$U/_wc\
|
$U/_wc\
|
||||||
$U/_zombie\
|
$U/_zombie\
|
||||||
|
|
||||||
|
@ -153,9 +156,9 @@ ifndef CPUS
|
||||||
CPUS := 3
|
CPUS := 3
|
||||||
endif
|
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 128M -smp $(CPUS) -nographic
|
||||||
QEMUOPTS = -machine virt -bios none -kernel $K/kernel -m 3G -smp $(CPUS) -nographic
|
QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0
|
||||||
QEMUOPTS += -drive file=fs.img,if=none,format=raw,id=x0 -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
|
QEMUOPTS += -device virtio-blk-device,drive=x0,bus=virtio-mmio-bus.0
|
||||||
|
|
||||||
qemu: $K/kernel fs.img
|
qemu: $K/kernel fs.img
|
||||||
$(QEMU) $(QEMUOPTS)
|
$(QEMU) $(QEMUOPTS)
|
||||||
|
|
|
@ -28,7 +28,8 @@ struct {
|
||||||
struct buf buf[NBUF];
|
struct buf buf[NBUF];
|
||||||
|
|
||||||
// Linked list of all buffers, through prev/next.
|
// 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;
|
struct buf head;
|
||||||
} bcache;
|
} 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){
|
for(b = bcache.head.prev; b != &bcache.head; b = b->prev){
|
||||||
if(b->refcnt == 0) {
|
if(b->refcnt == 0) {
|
||||||
b->dev = dev;
|
b->dev = dev;
|
||||||
|
@ -110,7 +112,7 @@ bwrite(struct buf *b)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Release a locked buffer.
|
// Release a locked buffer.
|
||||||
// Move to the head of the MRU list.
|
// Move to the head of the most-recently-used list.
|
||||||
void
|
void
|
||||||
brelse(struct buf *b)
|
brelse(struct buf *b)
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,7 +7,6 @@ struct buf {
|
||||||
uint refcnt;
|
uint refcnt;
|
||||||
struct buf *prev; // LRU cache list
|
struct buf *prev; // LRU cache list
|
||||||
struct buf *next;
|
struct buf *next;
|
||||||
struct buf *qnext; // disk queue
|
|
||||||
uchar data[BSIZE];
|
uchar data[BSIZE];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,8 @@
|
||||||
|
|
||||||
//
|
//
|
||||||
// send one character to the uart.
|
// send one character to the uart.
|
||||||
|
// called by printf, and to echo input characters,
|
||||||
|
// but not from write().
|
||||||
//
|
//
|
||||||
void
|
void
|
||||||
consputc(int c)
|
consputc(int c)
|
||||||
|
@ -40,9 +42,9 @@ consputc(int c)
|
||||||
|
|
||||||
if(c == BACKSPACE){
|
if(c == BACKSPACE){
|
||||||
// if the user typed backspace, overwrite with a space.
|
// if the user typed backspace, overwrite with a space.
|
||||||
uartputc('\b'); uartputc(' '); uartputc('\b');
|
uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b');
|
||||||
} else {
|
} else {
|
||||||
uartputc(c);
|
uartputc_sync(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,11 +72,11 @@ consolewrite(int user_src, uint64 src, int n)
|
||||||
char c;
|
char c;
|
||||||
if(either_copyin(&c, user_src, src+i, 1) == -1)
|
if(either_copyin(&c, user_src, src+i, 1) == -1)
|
||||||
break;
|
break;
|
||||||
consputc(c);
|
uartputc(c);
|
||||||
}
|
}
|
||||||
release(&cons.lock);
|
release(&cons.lock);
|
||||||
|
|
||||||
return n;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -52,6 +52,7 @@ struct inode* nameiparent(char*, char*);
|
||||||
int readi(struct inode*, int, uint64, uint, uint);
|
int readi(struct inode*, int, uint64, uint, uint);
|
||||||
void stati(struct inode*, struct stat*);
|
void stati(struct inode*, struct stat*);
|
||||||
int writei(struct inode*, int, uint64, uint, uint);
|
int writei(struct inode*, int, uint64, uint, uint);
|
||||||
|
void itrunc(struct inode*);
|
||||||
|
|
||||||
// ramdisk.c
|
// ramdisk.c
|
||||||
void ramdiskinit(void);
|
void ramdiskinit(void);
|
||||||
|
@ -149,6 +150,7 @@ void usertrapret(void);
|
||||||
void uartinit(void);
|
void uartinit(void);
|
||||||
void uartintr(void);
|
void uartintr(void);
|
||||||
void uartputc(int);
|
void uartputc(int);
|
||||||
|
void uartputc_sync(int);
|
||||||
int uartgetc(void);
|
int uartgetc(void);
|
||||||
|
|
||||||
// vm.c
|
// vm.c
|
||||||
|
@ -173,7 +175,6 @@ int copyinstr(pagetable_t, char *, uint64, uint64);
|
||||||
// plic.c
|
// plic.c
|
||||||
void plicinit(void);
|
void plicinit(void);
|
||||||
void plicinithart(void);
|
void plicinithart(void);
|
||||||
uint64 plic_pending(void);
|
|
||||||
int plic_claim(void);
|
int plic_claim(void);
|
||||||
void plic_complete(int);
|
void plic_complete(int);
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ exec(char *path, char **argv)
|
||||||
// arguments to user main(argc, argv)
|
// arguments to user main(argc, argv)
|
||||||
// argc is returned via the system call return
|
// argc is returned via the system call return
|
||||||
// value, which goes in a0.
|
// value, which goes in a0.
|
||||||
p->tf->a1 = sp;
|
p->trapframe->a1 = sp;
|
||||||
|
|
||||||
// Save program name for debugging.
|
// Save program name for debugging.
|
||||||
for(last=s=path; *s; s++)
|
for(last=s=path; *s; s++)
|
||||||
|
@ -109,9 +109,10 @@ exec(char *path, char **argv)
|
||||||
oldpagetable = p->pagetable;
|
oldpagetable = p->pagetable;
|
||||||
p->pagetable = pagetable;
|
p->pagetable = pagetable;
|
||||||
p->sz = sz;
|
p->sz = sz;
|
||||||
p->tf->epc = elf.entry; // initial program counter = main
|
p->trapframe->epc = elf.entry; // initial program counter = main
|
||||||
p->tf->sp = sp; // initial stack pointer
|
p->trapframe->sp = sp; // initial stack pointer
|
||||||
proc_freepagetable(oldpagetable, oldsz);
|
proc_freepagetable(oldpagetable, oldsz);
|
||||||
|
|
||||||
return argc; // this ends up in a0, the first argument to main(argc, argv)
|
return argc; // this ends up in a0, the first argument to main(argc, argv)
|
||||||
|
|
||||||
bad:
|
bad:
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
#define O_WRONLY 0x001
|
#define O_WRONLY 0x001
|
||||||
#define O_RDWR 0x002
|
#define O_RDWR 0x002
|
||||||
#define O_CREATE 0x200
|
#define O_CREATE 0x200
|
||||||
|
#define O_TRUNC 0x400
|
||||||
|
|
12
kernel/fs.c
12
kernel/fs.c
|
@ -22,7 +22,6 @@
|
||||||
#include "file.h"
|
#include "file.h"
|
||||||
|
|
||||||
#define min(a, b) ((a) < (b) ? (a) : (b))
|
#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
|
// there should be one superblock per disk device, but we run with
|
||||||
// only one device
|
// only one device
|
||||||
struct superblock sb;
|
struct superblock sb;
|
||||||
|
@ -406,11 +405,8 @@ bmap(struct inode *ip, uint bn)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Truncate inode (discard contents).
|
// Truncate inode (discard contents).
|
||||||
// Only called when the inode has no links
|
// Caller must hold ip->lock.
|
||||||
// to it (no directory entries referring to it)
|
void
|
||||||
// and has no in-memory reference to it (is
|
|
||||||
// not an open file or current directory).
|
|
||||||
static void
|
|
||||||
itrunc(struct inode *ip)
|
itrunc(struct inode *ip)
|
||||||
{
|
{
|
||||||
int i, j;
|
int i, j;
|
||||||
|
@ -463,7 +459,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n)
|
||||||
struct buf *bp;
|
struct buf *bp;
|
||||||
|
|
||||||
if(off > ip->size || off + n < off)
|
if(off > ip->size || off + n < off)
|
||||||
return -1;
|
return 0;
|
||||||
if(off + n > ip->size)
|
if(off + n > ip->size)
|
||||||
n = ip->size - off;
|
n = ip->size - off;
|
||||||
|
|
||||||
|
@ -476,7 +472,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n)
|
||||||
}
|
}
|
||||||
brelse(bp);
|
brelse(bp);
|
||||||
}
|
}
|
||||||
return n;
|
return tot;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write data to inode.
|
// Write data to inode.
|
||||||
|
|
|
@ -8,25 +8,37 @@ SECTIONS
|
||||||
* where qemu's -kernel jumps.
|
* where qemu's -kernel jumps.
|
||||||
*/
|
*/
|
||||||
. = 0x80000000;
|
. = 0x80000000;
|
||||||
.text :
|
|
||||||
{
|
.text : {
|
||||||
*(.text)
|
*(.text .text.*)
|
||||||
. = ALIGN(0x1000);
|
. = ALIGN(0x1000);
|
||||||
|
_trampoline = .;
|
||||||
*(trampsec)
|
*(trampsec)
|
||||||
|
. = ALIGN(0x1000);
|
||||||
|
ASSERT(. - _trampoline == 0x1000, "error: trampoline larger than one page");
|
||||||
|
PROVIDE(etext = .);
|
||||||
}
|
}
|
||||||
|
|
||||||
. = ALIGN(0x1000);
|
.rodata : {
|
||||||
PROVIDE(etext = .);
|
. = 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 : {
|
||||||
*(.data)
|
. = ALIGN(16);
|
||||||
|
*(.sdata .sdata.*) /* do not need to distinguish this from .data */
|
||||||
|
. = ALIGN(16);
|
||||||
|
*(.data .data.*)
|
||||||
}
|
}
|
||||||
|
|
||||||
.bss : {
|
.bss : {
|
||||||
*(.bss)
|
. = ALIGN(16);
|
||||||
*(.sbss*)
|
*(.sbss .sbss.*) /* do not need to distinguish this from .bss */
|
||||||
PROVIDE(end = .);
|
. = ALIGN(16);
|
||||||
|
*(.bss .bss.*)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PROVIDE(end = .);
|
||||||
}
|
}
|
||||||
|
|
|
@ -62,6 +62,6 @@
|
||||||
// fixed-size stack
|
// fixed-size stack
|
||||||
// expandable heap
|
// expandable heap
|
||||||
// ...
|
// ...
|
||||||
// TRAPFRAME (p->tf, used by the trampoline)
|
// TRAPFRAME (p->trapframe, used by the trampoline)
|
||||||
// TRAMPOLINE (the same page as in the kernel)
|
// TRAMPOLINE (the same page as in the kernel)
|
||||||
#define TRAPFRAME (TRAMPOLINE - PGSIZE)
|
#define TRAPFRAME (TRAMPOLINE - PGSIZE)
|
||||||
|
|
|
@ -83,7 +83,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n)
|
||||||
acquire(&pi->lock);
|
acquire(&pi->lock);
|
||||||
for(i = 0; i < n; i++){
|
for(i = 0; i < n; i++){
|
||||||
while(pi->nwrite == pi->nread + PIPESIZE){ //DOC: pipewrite-full
|
while(pi->nwrite == pi->nread + PIPESIZE){ //DOC: pipewrite-full
|
||||||
if(pi->readopen == 0 || myproc()->killed){
|
if(pi->readopen == 0 || pr->killed){
|
||||||
release(&pi->lock);
|
release(&pi->lock);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n)
|
||||||
}
|
}
|
||||||
wakeup(&pi->nread);
|
wakeup(&pi->nread);
|
||||||
release(&pi->lock);
|
release(&pi->lock);
|
||||||
return n;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
|
@ -108,7 +108,7 @@ piperead(struct pipe *pi, uint64 addr, int n)
|
||||||
|
|
||||||
acquire(&pi->lock);
|
acquire(&pi->lock);
|
||||||
while(pi->nread == pi->nwrite && pi->writeopen){ //DOC: pipe-empty
|
while(pi->nread == pi->nwrite && pi->writeopen){ //DOC: pipe-empty
|
||||||
if(myproc()->killed){
|
if(pr->killed){
|
||||||
release(&pi->lock);
|
release(&pi->lock);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,26 +28,11 @@ plicinithart(void)
|
||||||
*(uint32*)PLIC_SPRIORITY(hart) = 0;
|
*(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.
|
// ask the PLIC what interrupt we should serve.
|
||||||
int
|
int
|
||||||
plic_claim(void)
|
plic_claim(void)
|
||||||
{
|
{
|
||||||
int hart = cpuid();
|
int hart = cpuid();
|
||||||
//int irq = *(uint32*)(PLIC + 0x201004);
|
|
||||||
int irq = *(uint32*)PLIC_SCLAIM(hart);
|
int irq = *(uint32*)PLIC_SCLAIM(hart);
|
||||||
return irq;
|
return irq;
|
||||||
}
|
}
|
||||||
|
@ -57,6 +42,5 @@ void
|
||||||
plic_complete(int irq)
|
plic_complete(int irq)
|
||||||
{
|
{
|
||||||
int hart = cpuid();
|
int hart = cpuid();
|
||||||
//*(uint32*)(PLIC + 0x201004) = irq;
|
|
||||||
*(uint32*)PLIC_SCLAIM(hart) = irq;
|
*(uint32*)PLIC_SCLAIM(hart) = irq;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ static void wakeup1(struct proc *chan);
|
||||||
|
|
||||||
extern char trampoline[]; // trampoline.S
|
extern char trampoline[]; // trampoline.S
|
||||||
|
|
||||||
|
// initialize the proc table at boot time.
|
||||||
void
|
void
|
||||||
procinit(void)
|
procinit(void)
|
||||||
{
|
{
|
||||||
|
@ -106,7 +107,7 @@ found:
|
||||||
p->pid = allocpid();
|
p->pid = allocpid();
|
||||||
|
|
||||||
// Allocate a trapframe page.
|
// Allocate a trapframe page.
|
||||||
if((p->tf = (struct trapframe *)kalloc()) == 0){
|
if((p->trapframe = (struct trapframe *)kalloc()) == 0){
|
||||||
release(&p->lock);
|
release(&p->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -116,7 +117,7 @@ found:
|
||||||
|
|
||||||
// Set up new context to start executing at forkret,
|
// Set up new context to start executing at forkret,
|
||||||
// which returns to user space.
|
// 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.ra = (uint64)forkret;
|
||||||
p->context.sp = p->kstack + PGSIZE;
|
p->context.sp = p->kstack + PGSIZE;
|
||||||
|
|
||||||
|
@ -129,9 +130,9 @@ found:
|
||||||
static void
|
static void
|
||||||
freeproc(struct proc *p)
|
freeproc(struct proc *p)
|
||||||
{
|
{
|
||||||
if(p->tf)
|
if(p->trapframe)
|
||||||
kfree((void*)p->tf);
|
kfree((void*)p->trapframe);
|
||||||
p->tf = 0;
|
p->trapframe = 0;
|
||||||
if(p->pagetable)
|
if(p->pagetable)
|
||||||
proc_freepagetable(p->pagetable, p->sz);
|
proc_freepagetable(p->pagetable, p->sz);
|
||||||
p->pagetable = 0;
|
p->pagetable = 0;
|
||||||
|
@ -145,8 +146,8 @@ freeproc(struct proc *p)
|
||||||
p->state = UNUSED;
|
p->state = UNUSED;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a page table for a given process,
|
// Create a user page table for a given process,
|
||||||
// with no user pages, but with trampoline pages.
|
// with no user memory, but with trampoline pages.
|
||||||
pagetable_t
|
pagetable_t
|
||||||
proc_pagetable(struct proc *p)
|
proc_pagetable(struct proc *p)
|
||||||
{
|
{
|
||||||
|
@ -164,7 +165,7 @@ proc_pagetable(struct proc *p)
|
||||||
|
|
||||||
// map the trapframe just below TRAMPOLINE, for trampoline.S.
|
// map the trapframe just below TRAMPOLINE, for trampoline.S.
|
||||||
mappages(pagetable, TRAPFRAME, PGSIZE,
|
mappages(pagetable, TRAPFRAME, PGSIZE,
|
||||||
(uint64)(p->tf), PTE_R | PTE_W);
|
(uint64)(p->trapframe), PTE_R | PTE_W);
|
||||||
|
|
||||||
return pagetable;
|
return pagetable;
|
||||||
}
|
}
|
||||||
|
@ -176,8 +177,7 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz)
|
||||||
{
|
{
|
||||||
uvmunmap(pagetable, TRAMPOLINE, PGSIZE, 0);
|
uvmunmap(pagetable, TRAMPOLINE, PGSIZE, 0);
|
||||||
uvmunmap(pagetable, TRAPFRAME, PGSIZE, 0);
|
uvmunmap(pagetable, TRAPFRAME, PGSIZE, 0);
|
||||||
if(sz > 0)
|
uvmfree(pagetable, sz);
|
||||||
uvmfree(pagetable, sz);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// a user program that calls exec("/init")
|
// a user program that calls exec("/init")
|
||||||
|
@ -207,8 +207,8 @@ userinit(void)
|
||||||
p->sz = PGSIZE;
|
p->sz = PGSIZE;
|
||||||
|
|
||||||
// prepare for the very first "return" from kernel to user.
|
// prepare for the very first "return" from kernel to user.
|
||||||
p->tf->epc = 0; // user program counter
|
p->trapframe->epc = 0; // user program counter
|
||||||
p->tf->sp = PGSIZE; // user stack pointer
|
p->trapframe->sp = PGSIZE; // user stack pointer
|
||||||
|
|
||||||
safestrcpy(p->name, "initcode", sizeof(p->name));
|
safestrcpy(p->name, "initcode", sizeof(p->name));
|
||||||
p->cwd = namei("/");
|
p->cwd = namei("/");
|
||||||
|
@ -263,10 +263,10 @@ fork(void)
|
||||||
np->parent = p;
|
np->parent = p;
|
||||||
|
|
||||||
// copy saved user registers.
|
// copy saved user registers.
|
||||||
*(np->tf) = *(p->tf);
|
*(np->trapframe) = *(p->trapframe);
|
||||||
|
|
||||||
// Cause fork to return 0 in the child.
|
// Cause fork to return 0 in the child.
|
||||||
np->tf->a0 = 0;
|
np->trapframe->a0 = 0;
|
||||||
|
|
||||||
// increment reference counts on open file descriptors.
|
// increment reference counts on open file descriptors.
|
||||||
for(i = 0; i < NOFILE; i++)
|
for(i = 0; i < NOFILE; i++)
|
||||||
|
@ -457,7 +457,7 @@ scheduler(void)
|
||||||
// before jumping back to us.
|
// before jumping back to us.
|
||||||
p->state = RUNNING;
|
p->state = RUNNING;
|
||||||
c->proc = p;
|
c->proc = p;
|
||||||
swtch(&c->scheduler, &p->context);
|
swtch(&c->context, &p->context);
|
||||||
|
|
||||||
// Process is done running for now.
|
// Process is done running for now.
|
||||||
// It should have changed its p->state before coming back.
|
// It should have changed its p->state before coming back.
|
||||||
|
@ -491,7 +491,7 @@ sched(void)
|
||||||
panic("sched interruptible");
|
panic("sched interruptible");
|
||||||
|
|
||||||
intena = mycpu()->intena;
|
intena = mycpu()->intena;
|
||||||
swtch(&p->context, &mycpu()->scheduler);
|
swtch(&p->context, &mycpu()->context);
|
||||||
mycpu()->intena = intena;
|
mycpu()->intena = intena;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ struct context {
|
||||||
// Per-CPU state.
|
// Per-CPU state.
|
||||||
struct cpu {
|
struct cpu {
|
||||||
struct proc *proc; // The process running on this cpu, or null.
|
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 noff; // Depth of push_off() nesting.
|
||||||
int intena; // Were interrupts enabled before push_off()?
|
int intena; // Were interrupts enabled before push_off()?
|
||||||
};
|
};
|
||||||
|
@ -95,10 +95,10 @@ struct proc {
|
||||||
int pid; // Process ID
|
int pid; // Process ID
|
||||||
|
|
||||||
// these are private to the process, so p->lock need not be held.
|
// 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)
|
uint64 sz; // Size of process memory (bytes)
|
||||||
pagetable_t pagetable; // Page table
|
pagetable_t pagetable; // User page table
|
||||||
struct trapframe *tf; // data page for trampoline.S
|
struct trapframe *trapframe; // data page for trampoline.S
|
||||||
struct context context; // swtch() here to run process
|
struct context context; // swtch() here to run process
|
||||||
struct file *ofile[NOFILE]; // Open files
|
struct file *ofile[NOFILE]; // Open files
|
||||||
struct inode *cwd; // Current directory
|
struct inode *cwd; // Current directory
|
||||||
|
|
|
@ -261,7 +261,6 @@ r_time()
|
||||||
static inline void
|
static inline void
|
||||||
intr_on()
|
intr_on()
|
||||||
{
|
{
|
||||||
w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
|
|
||||||
w_sstatus(r_sstatus() | SSTATUS_SIE);
|
w_sstatus(r_sstatus() | SSTATUS_SIE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,8 @@ acquire(struct spinlock *lk)
|
||||||
|
|
||||||
// Tell the C compiler and the processor to not move loads or stores
|
// Tell the C compiler and the processor to not move loads or stores
|
||||||
// past this point, to ensure that the critical section's memory
|
// 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();
|
__sync_synchronize();
|
||||||
|
|
||||||
// Record info about lock acquisition for holding() and debugging.
|
// 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
|
// 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
|
// past this point, to ensure that all the stores in the critical
|
||||||
// section are visible to other CPUs before the lock is released.
|
// section are visible to other CPUs before the lock is released,
|
||||||
// On RISC-V, this turns into a fence instruction.
|
// and that loads in the critical section occur strictly before
|
||||||
|
// the lock is released.
|
||||||
|
// On RISC-V, this emits a fence instruction.
|
||||||
__sync_synchronize();
|
__sync_synchronize();
|
||||||
|
|
||||||
// Release the lock, equivalent to lk->locked = 0.
|
// Release the lock, equivalent to lk->locked = 0.
|
||||||
|
@ -69,13 +72,12 @@ release(struct spinlock *lk)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether this cpu is holding the lock.
|
// Check whether this cpu is holding the lock.
|
||||||
|
// Interrupts must be off.
|
||||||
int
|
int
|
||||||
holding(struct spinlock *lk)
|
holding(struct spinlock *lk)
|
||||||
{
|
{
|
||||||
int r;
|
int r;
|
||||||
push_off();
|
|
||||||
r = (lk->locked && lk->cpu == mycpu());
|
r = (lk->locked && lk->cpu == mycpu());
|
||||||
pop_off();
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,9 +102,9 @@ pop_off(void)
|
||||||
struct cpu *c = mycpu();
|
struct cpu *c = mycpu();
|
||||||
if(intr_get())
|
if(intr_get())
|
||||||
panic("pop_off - interruptible");
|
panic("pop_off - interruptible");
|
||||||
c->noff -= 1;
|
if(c->noff < 1)
|
||||||
if(c->noff < 0)
|
|
||||||
panic("pop_off");
|
panic("pop_off");
|
||||||
|
c->noff -= 1;
|
||||||
if(c->noff == 0 && c->intena)
|
if(c->noff == 0 && c->intena)
|
||||||
intr_on();
|
intr_on();
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ start()
|
||||||
// delegate all interrupts and exceptions to supervisor mode.
|
// delegate all interrupts and exceptions to supervisor mode.
|
||||||
w_medeleg(0xffff);
|
w_medeleg(0xffff);
|
||||||
w_mideleg(0xffff);
|
w_mideleg(0xffff);
|
||||||
|
w_sie(r_sie() | SIE_SEIE | SIE_STIE | SIE_SSIE);
|
||||||
|
|
||||||
// ask for clock interrupts.
|
// ask for clock interrupts.
|
||||||
timerinit();
|
timerinit();
|
||||||
|
|
|
@ -37,17 +37,17 @@ argraw(int n)
|
||||||
struct proc *p = myproc();
|
struct proc *p = myproc();
|
||||||
switch (n) {
|
switch (n) {
|
||||||
case 0:
|
case 0:
|
||||||
return p->tf->a0;
|
return p->trapframe->a0;
|
||||||
case 1:
|
case 1:
|
||||||
return p->tf->a1;
|
return p->trapframe->a1;
|
||||||
case 2:
|
case 2:
|
||||||
return p->tf->a2;
|
return p->trapframe->a2;
|
||||||
case 3:
|
case 3:
|
||||||
return p->tf->a3;
|
return p->trapframe->a3;
|
||||||
case 4:
|
case 4:
|
||||||
return p->tf->a4;
|
return p->trapframe->a4;
|
||||||
case 5:
|
case 5:
|
||||||
return p->tf->a5;
|
return p->trapframe->a5;
|
||||||
}
|
}
|
||||||
panic("argraw");
|
panic("argraw");
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -135,12 +135,12 @@ syscall(void)
|
||||||
int num;
|
int num;
|
||||||
struct proc *p = myproc();
|
struct proc *p = myproc();
|
||||||
|
|
||||||
num = p->tf->a7;
|
num = p->trapframe->a7;
|
||||||
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
|
if(num > 0 && num < NELEM(syscalls) && syscalls[num]) {
|
||||||
p->tf->a0 = syscalls[num]();
|
p->trapframe->a0 = syscalls[num]();
|
||||||
} else {
|
} else {
|
||||||
printf("%d %s: unknown sys call %d\n",
|
printf("%d %s: unknown sys call %d\n",
|
||||||
p->pid, p->name, num);
|
p->pid, p->name, num);
|
||||||
p->tf->a0 = -1;
|
p->trapframe->a0 = -1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -341,6 +341,10 @@ sys_open(void)
|
||||||
f->readable = !(omode & O_WRONLY);
|
f->readable = !(omode & O_WRONLY);
|
||||||
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
|
f->writable = (omode & O_WRONLY) || (omode & O_RDWR);
|
||||||
|
|
||||||
|
if((omode & O_TRUNC) && ip->type == T_FILE){
|
||||||
|
itrunc(ip);
|
||||||
|
}
|
||||||
|
|
||||||
iunlock(ip);
|
iunlock(ip);
|
||||||
end_op();
|
end_op();
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,7 @@ uservec:
|
||||||
# in supervisor mode, but with a
|
# in supervisor mode, but with a
|
||||||
# user page table.
|
# 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.
|
# mapped into user space, at TRAPFRAME.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -60,20 +60,20 @@ uservec:
|
||||||
sd t5, 272(a0)
|
sd t5, 272(a0)
|
||||||
sd t6, 280(a0)
|
sd t6, 280(a0)
|
||||||
|
|
||||||
# save the user a0 in p->tf->a0
|
# save the user a0 in p->trapframe->a0
|
||||||
csrr t0, sscratch
|
csrr t0, sscratch
|
||||||
sd t0, 112(a0)
|
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)
|
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)
|
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)
|
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)
|
ld t1, 0(a0)
|
||||||
csrw satp, t1
|
csrw satp, t1
|
||||||
sfence.vma zero, zero
|
sfence.vma zero, zero
|
||||||
|
|
|
@ -48,7 +48,7 @@ usertrap(void)
|
||||||
struct proc *p = myproc();
|
struct proc *p = myproc();
|
||||||
|
|
||||||
// save user program counter.
|
// save user program counter.
|
||||||
p->tf->epc = r_sepc();
|
p->trapframe->epc = r_sepc();
|
||||||
|
|
||||||
if(r_scause() == 8){
|
if(r_scause() == 8){
|
||||||
// system call
|
// system call
|
||||||
|
@ -58,7 +58,7 @@ usertrap(void)
|
||||||
|
|
||||||
// sepc points to the ecall instruction,
|
// sepc points to the ecall instruction,
|
||||||
// but we want to return to the next 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,
|
// an interrupt will change sstatus &c registers,
|
||||||
// so don't enable until done with those registers.
|
// so don't enable until done with those registers.
|
||||||
|
@ -91,8 +91,9 @@ usertrapret(void)
|
||||||
{
|
{
|
||||||
struct proc *p = myproc();
|
struct proc *p = myproc();
|
||||||
|
|
||||||
// turn off interrupts, since we're switching
|
// we're about to switch the destination of traps from
|
||||||
// now from kerneltrap() to usertrap().
|
// kerneltrap() to usertrap(), so turn off interrupts until
|
||||||
|
// we're back in user space, where usertrap() is correct.
|
||||||
intr_off();
|
intr_off();
|
||||||
|
|
||||||
// send syscalls, interrupts, and exceptions to trampoline.S
|
// send syscalls, interrupts, and exceptions to trampoline.S
|
||||||
|
@ -100,10 +101,10 @@ usertrapret(void)
|
||||||
|
|
||||||
// set up trapframe values that uservec will need when
|
// set up trapframe values that uservec will need when
|
||||||
// the process next re-enters the kernel.
|
// the process next re-enters the kernel.
|
||||||
p->tf->kernel_satp = r_satp(); // kernel page table
|
p->trapframe->kernel_satp = r_satp(); // kernel page table
|
||||||
p->tf->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
|
p->trapframe->kernel_sp = p->kstack + PGSIZE; // process's kernel stack
|
||||||
p->tf->kernel_trap = (uint64)usertrap;
|
p->trapframe->kernel_trap = (uint64)usertrap;
|
||||||
p->tf->kernel_hartid = r_tp(); // hartid for cpuid()
|
p->trapframe->kernel_hartid = r_tp(); // hartid for cpuid()
|
||||||
|
|
||||||
// set up the registers that trampoline.S's sret will use
|
// set up the registers that trampoline.S's sret will use
|
||||||
// to get to user space.
|
// to get to user space.
|
||||||
|
@ -115,7 +116,7 @@ usertrapret(void)
|
||||||
w_sstatus(x);
|
w_sstatus(x);
|
||||||
|
|
||||||
// set S Exception Program Counter to the saved user pc.
|
// 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.
|
// tell trampoline.S the user page table to switch to.
|
||||||
uint64 satp = MAKE_SATP(p->pagetable);
|
uint64 satp = MAKE_SATP(p->pagetable);
|
||||||
|
@ -129,7 +130,6 @@ usertrapret(void)
|
||||||
|
|
||||||
// interrupts and exceptions from kernel code go here via kernelvec,
|
// interrupts and exceptions from kernel code go here via kernelvec,
|
||||||
// on whatever the current kernel stack is.
|
// on whatever the current kernel stack is.
|
||||||
// must be 4-byte aligned to fit in stvec.
|
|
||||||
void
|
void
|
||||||
kerneltrap()
|
kerneltrap()
|
||||||
{
|
{
|
||||||
|
@ -189,9 +189,16 @@ devintr()
|
||||||
uartintr();
|
uartintr();
|
||||||
} else if(irq == VIRTIO0_IRQ){
|
} else if(irq == VIRTIO0_IRQ){
|
||||||
virtio_disk_intr();
|
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;
|
return 1;
|
||||||
} else if(scause == 0x8000000000000001L){
|
} else if(scause == 0x8000000000000001L){
|
||||||
// software interrupt from a machine-mode timer interrupt,
|
// software interrupt from a machine-mode timer interrupt,
|
||||||
|
|
121
kernel/uart.c
121
kernel/uart.c
|
@ -18,18 +18,35 @@
|
||||||
// the UART control registers.
|
// the UART control registers.
|
||||||
// some have different meanings for
|
// some have different meanings for
|
||||||
// read vs write.
|
// read vs write.
|
||||||
// http://byterunner.com/16550.html
|
// see http://byterunner.com/16550.html
|
||||||
#define RHR 0 // receive holding register (for input bytes)
|
#define RHR 0 // receive holding register (for input bytes)
|
||||||
#define THR 0 // transmit holding register (for output bytes)
|
#define THR 0 // transmit holding register (for output bytes)
|
||||||
#define IER 1 // interrupt enable register
|
#define IER 1 // interrupt enable register
|
||||||
#define FCR 2 // FIFO control register
|
#define IER_TX_ENABLE (1<<0)
|
||||||
#define ISR 2 // interrupt status register
|
#define IER_RX_ENABLE (1<<1)
|
||||||
#define LCR 3 // line control register
|
#define FCR 2 // FIFO control register
|
||||||
#define LSR 5 // line status 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 ReadReg(reg) (*(Reg(reg)))
|
||||||
#define WriteReg(reg, v) (*(Reg(reg)) = (v))
|
#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
|
void
|
||||||
uartinit(void)
|
uartinit(void)
|
||||||
{
|
{
|
||||||
|
@ -37,7 +54,7 @@ uartinit(void)
|
||||||
WriteReg(IER, 0x00);
|
WriteReg(IER, 0x00);
|
||||||
|
|
||||||
// special mode to set baud rate.
|
// special mode to set baud rate.
|
||||||
WriteReg(LCR, 0x80);
|
WriteReg(LCR, LCR_BAUD_LATCH);
|
||||||
|
|
||||||
// LSB for baud rate of 38.4K.
|
// LSB for baud rate of 38.4K.
|
||||||
WriteReg(0, 0x03);
|
WriteReg(0, 0x03);
|
||||||
|
@ -47,23 +64,87 @@ uartinit(void)
|
||||||
|
|
||||||
// leave set-baud mode,
|
// leave set-baud mode,
|
||||||
// and set word length to 8 bits, no parity.
|
// and set word length to 8 bits, no parity.
|
||||||
WriteReg(LCR, 0x03);
|
WriteReg(LCR, LCR_EIGHT_BITS);
|
||||||
|
|
||||||
// reset and enable FIFOs.
|
// reset and enable FIFOs.
|
||||||
WriteReg(FCR, 0x07);
|
WriteReg(FCR, FCR_FIFO_ENABLE | FCR_FIFO_CLEAR);
|
||||||
|
|
||||||
// enable receive interrupts.
|
// enable transmit and receive interrupts.
|
||||||
WriteReg(IER, 0x01);
|
WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE);
|
||||||
|
|
||||||
|
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
|
void
|
||||||
uartputc(int c)
|
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.
|
// 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);
|
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.
|
// 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
|
void
|
||||||
uartintr(void)
|
uartintr(void)
|
||||||
{
|
{
|
||||||
|
// read and process incoming characters.
|
||||||
while(1){
|
while(1){
|
||||||
int c = uartgetc();
|
int c = uartgetc();
|
||||||
if(c == -1)
|
if(c == -1)
|
||||||
break;
|
break;
|
||||||
consoleintr(c);
|
consoleintr(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// send buffered characters.
|
||||||
|
acquire(&uart_tx_lock);
|
||||||
|
uartstart();
|
||||||
|
release(&uart_tx_lock);
|
||||||
}
|
}
|
||||||
|
|
11
kernel/vm.c
11
kernel/vm.c
|
@ -16,9 +16,7 @@ extern char etext[]; // kernel.ld sets this to end of kernel code.
|
||||||
extern char trampoline[]; // trampoline.S
|
extern char trampoline[]; // trampoline.S
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* create a direct-map page table for the kernel and
|
* create a direct-map page table for the kernel.
|
||||||
* turn on paging. called early, in supervisor mode.
|
|
||||||
* the page allocator is already initialized.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
kvminit()
|
kvminit()
|
||||||
|
@ -70,7 +68,7 @@ kvminithart()
|
||||||
// 21..29 -- 9 bits of level-1 index.
|
// 21..29 -- 9 bits of level-1 index.
|
||||||
// 12..20 -- 9 bits of level-0 index.
|
// 12..20 -- 9 bits of level-0 index.
|
||||||
// 0..11 -- 12 bits of byte offset within the page.
|
// 0..11 -- 12 bits of byte offset within the page.
|
||||||
static pte_t *
|
pte_t *
|
||||||
walk(pagetable_t pagetable, uint64 va, int alloc)
|
walk(pagetable_t pagetable, uint64 va, int alloc)
|
||||||
{
|
{
|
||||||
if(va >= MAXVA)
|
if(va >= MAXVA)
|
||||||
|
@ -278,7 +276,7 @@ uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz)
|
||||||
|
|
||||||
// Recursively free page-table pages.
|
// Recursively free page-table pages.
|
||||||
// All leaf mappings must already have been removed.
|
// All leaf mappings must already have been removed.
|
||||||
static void
|
void
|
||||||
freewalk(pagetable_t pagetable)
|
freewalk(pagetable_t pagetable)
|
||||||
{
|
{
|
||||||
// there are 2^9 = 512 PTEs in a page table.
|
// there are 2^9 = 512 PTEs in a page table.
|
||||||
|
@ -301,7 +299,8 @@ freewalk(pagetable_t pagetable)
|
||||||
void
|
void
|
||||||
uvmfree(pagetable_t pagetable, uint64 sz)
|
uvmfree(pagetable_t pagetable, uint64 sz)
|
||||||
{
|
{
|
||||||
uvmunmap(pagetable, 0, sz, 1);
|
if(sz > 0)
|
||||||
|
uvmunmap(pagetable, 0, sz, 1);
|
||||||
freewalk(pagetable);
|
freewalk(pagetable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -11,12 +11,12 @@ cat(int fd)
|
||||||
|
|
||||||
while((n = read(fd, buf, sizeof(buf))) > 0) {
|
while((n = read(fd, buf, sizeof(buf))) > 0) {
|
||||||
if (write(1, buf, n) != n) {
|
if (write(1, buf, n) != n) {
|
||||||
printf("cat: write error\n");
|
fprintf(2, "cat: write error\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(n < 0){
|
if(n < 0){
|
||||||
printf("cat: read error\n");
|
fprintf(2, "cat: read error\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,12 +28,12 @@ main(int argc, char *argv[])
|
||||||
|
|
||||||
if(argc <= 1){
|
if(argc <= 1){
|
||||||
cat(0);
|
cat(0);
|
||||||
exit(1);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i = 1; i < argc; i++){
|
for(i = 1; i < argc; i++){
|
||||||
if((fd = open(argv[i], 0)) < 0){
|
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);
|
exit(1);
|
||||||
}
|
}
|
||||||
cat(fd);
|
cat(fd);
|
||||||
|
|
333
user/grind.c
Normal file
333
user/grind.c
Normal file
|
@ -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);
|
||||||
|
}
|
|
@ -386,7 +386,7 @@ parseredirs(struct cmd *cmd, char **ps, char *es)
|
||||||
cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
|
cmd = redircmd(cmd, q, eq, O_RDONLY, 0);
|
||||||
break;
|
break;
|
||||||
case '>':
|
case '>':
|
||||||
cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
|
cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE|O_TRUNC, 1);
|
||||||
break;
|
break;
|
||||||
case '+': // >>
|
case '+': // >>
|
||||||
cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
|
cmd = redircmd(cmd, q, eq, O_WRONLY|O_CREATE, 1);
|
||||||
|
|
31
user/ulib.c
31
user/ulib.c
|
@ -103,7 +103,34 @@ memmove(void *vdst, const void *vsrc, int n)
|
||||||
|
|
||||||
dst = vdst;
|
dst = vdst;
|
||||||
src = vsrc;
|
src = vsrc;
|
||||||
while(n-- > 0)
|
if (src > dst) {
|
||||||
*dst++ = *src++;
|
while(n-- > 0)
|
||||||
|
*dst++ = *src++;
|
||||||
|
} else {
|
||||||
|
dst += n;
|
||||||
|
src += n;
|
||||||
|
while(n-- > 0)
|
||||||
|
*--dst = *--src;
|
||||||
|
}
|
||||||
return vdst;
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -38,3 +38,5 @@ void* memset(void*, int, uint);
|
||||||
void* malloc(uint);
|
void* malloc(uint);
|
||||||
void free(void*);
|
void free(void*);
|
||||||
int atoi(const char*);
|
int atoi(const char*);
|
||||||
|
int memcmp(const void *, const void *, uint);
|
||||||
|
void *memcpy(void *, const void *, uint);
|
||||||
|
|
471
user/usertests.c
471
user/usertests.c
|
@ -22,6 +22,352 @@
|
||||||
char buf[BUFSZ];
|
char buf[BUFSZ];
|
||||||
char name[3];
|
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?
|
// does chdir() call iput(p->cwd) in a transaction?
|
||||||
void
|
void
|
||||||
iputtest(char *s)
|
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));
|
close(open(file, 0));
|
||||||
|
close(open(file, 0));
|
||||||
|
close(open(file, 0));
|
||||||
} else {
|
} else {
|
||||||
unlink(file);
|
unlink(file);
|
||||||
unlink(file);
|
unlink(file);
|
||||||
unlink(file);
|
unlink(file);
|
||||||
unlink(file);
|
unlink(file);
|
||||||
|
unlink(file);
|
||||||
|
unlink(file);
|
||||||
}
|
}
|
||||||
if(pid == 0)
|
if(pid == 0)
|
||||||
exit(0);
|
exit(0);
|
||||||
|
@ -1106,7 +1456,7 @@ bigdir(char *s)
|
||||||
name[2] = '0' + (i % 64);
|
name[2] = '0' + (i % 64);
|
||||||
name[3] = '\0';
|
name[3] = '\0';
|
||||||
if(link("bd", name) != 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);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1335,8 +1685,8 @@ bigfile(char *s)
|
||||||
enum { N = 20, SZ=600 };
|
enum { N = 20, SZ=600 };
|
||||||
int fd, i, total, cc;
|
int fd, i, total, cc;
|
||||||
|
|
||||||
unlink("bigfile");
|
unlink("bigfile.dat");
|
||||||
fd = open("bigfile", O_CREATE | O_RDWR);
|
fd = open("bigfile.dat", O_CREATE | O_RDWR);
|
||||||
if(fd < 0){
|
if(fd < 0){
|
||||||
printf("%s: cannot create bigfile", s);
|
printf("%s: cannot create bigfile", s);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -1350,7 +1700,7 @@ bigfile(char *s)
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
||||||
fd = open("bigfile", 0);
|
fd = open("bigfile.dat", 0);
|
||||||
if(fd < 0){
|
if(fd < 0){
|
||||||
printf("%s: cannot open bigfile\n", s);
|
printf("%s: cannot open bigfile\n", s);
|
||||||
exit(1);
|
exit(1);
|
||||||
|
@ -1379,7 +1729,7 @@ bigfile(char *s)
|
||||||
printf("%s: read bigfile wrong total\n", s);
|
printf("%s: read bigfile wrong total\n", s);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
unlink("bigfile");
|
unlink("bigfile.dat");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1418,6 +1768,14 @@ fourteen(char *s)
|
||||||
printf("%s: mkdir 12345678901234/123456789012345 succeeded!\n", s);
|
printf("%s: mkdir 12345678901234/123456789012345 succeeded!\n", s);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
unlink("123456789012345/12345678901234");
|
||||||
|
unlink("12345678901234/12345678901234");
|
||||||
|
unlink("12345678901234/12345678901234/12345678901234");
|
||||||
|
unlink("123456789012345/123456789012345/123456789012345");
|
||||||
|
unlink("12345678901234/123456789012345");
|
||||||
|
unlink("12345678901234");
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1512,7 +1870,8 @@ dirfile(char *s)
|
||||||
close(fd);
|
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
|
void
|
||||||
iref(char *s)
|
iref(char *s)
|
||||||
{
|
{
|
||||||
|
@ -1539,6 +1898,12 @@ iref(char *s)
|
||||||
unlink("xx");
|
unlink("xx");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// clean up
|
||||||
|
for(i = 0; i < NINODE + 1; i++){
|
||||||
|
chdir("..");
|
||||||
|
unlink("irefd");
|
||||||
|
}
|
||||||
|
|
||||||
chdir("/");
|
chdir("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2087,13 +2452,35 @@ badarg(char *s)
|
||||||
exit(0);
|
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()
|
// run each test in its own process. run returns 1 if child's exit()
|
||||||
// indicates success.
|
// indicates success.
|
||||||
int
|
int
|
||||||
run(void f(char *), char *s) {
|
run(void f(char *), char *s) {
|
||||||
int pid;
|
int pid;
|
||||||
int xstatus;
|
int xstatus;
|
||||||
|
|
||||||
printf("test %s: ", s);
|
printf("test %s: ", s);
|
||||||
if((pid = fork()) < 0) {
|
if((pid = fork()) < 0) {
|
||||||
printf("runtest: fork error\n");
|
printf("runtest: fork error\n");
|
||||||
|
@ -2105,9 +2492,9 @@ run(void f(char *), char *s) {
|
||||||
} else {
|
} else {
|
||||||
wait(&xstatus);
|
wait(&xstatus);
|
||||||
if(xstatus != 0)
|
if(xstatus != 0)
|
||||||
printf("FAILED\n", s);
|
printf("FAILED\n");
|
||||||
else
|
else
|
||||||
printf("OK\n", s);
|
printf("OK\n");
|
||||||
return xstatus == 0;
|
return xstatus == 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2115,15 +2502,30 @@ run(void f(char *), char *s) {
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
char *n = 0;
|
int continuous = 0;
|
||||||
if(argc > 1) {
|
char *justone = 0;
|
||||||
n = argv[1];
|
|
||||||
|
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 {
|
struct test {
|
||||||
void (*f)(char *);
|
void (*f)(char *);
|
||||||
char *s;
|
char *s;
|
||||||
} tests[] = {
|
} tests[] = {
|
||||||
|
{copyin, "copyin"},
|
||||||
|
{copyout, "copyout"},
|
||||||
|
{copyinstr1, "copyinstr1"},
|
||||||
|
{copyinstr2, "copyinstr2"},
|
||||||
|
{copyinstr3, "copyinstr3"},
|
||||||
|
{truncate1, "truncate1"},
|
||||||
|
{truncate2, "truncate2"},
|
||||||
|
{truncate3, "truncate3"},
|
||||||
{reparent2, "reparent2"},
|
{reparent2, "reparent2"},
|
||||||
{pgbug, "pgbug" },
|
{pgbug, "pgbug" },
|
||||||
{sbrkbugs, "sbrkbugs" },
|
{sbrkbugs, "sbrkbugs" },
|
||||||
|
@ -2173,25 +2575,48 @@ main(int argc, char *argv[])
|
||||||
{bigdir, "bigdir"}, // slow
|
{bigdir, "bigdir"}, // slow
|
||||||
{ 0, 0},
|
{ 0, 0},
|
||||||
};
|
};
|
||||||
|
|
||||||
printf("usertests starting\n");
|
|
||||||
|
|
||||||
if(open("usertests.ran", 0) >= 0){
|
if(continuous){
|
||||||
printf("already ran user tests -- rebuild fs.img (rm fs.img; make fs.img)\n");
|
printf("continuous usertests starting\n");
|
||||||
exit(1);
|
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;
|
int fail = 0;
|
||||||
for (struct test *t = tests; t->s != 0; t++) {
|
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))
|
if(!run(t->f, t->s))
|
||||||
fail = 1;
|
fail = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!fail)
|
|
||||||
printf("ALL TESTS PASSED\n");
|
if(fail){
|
||||||
else
|
|
||||||
printf("SOME TESTS FAILED\n");
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue