From 73e931fd420163cb3e7757a3426ca650b4cd3419 Mon Sep 17 00:00:00 2001 From: rtm Date: Thu, 7 Nov 2019 06:43:38 -0500 Subject: yet another toolchain name --- Makefile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Makefile b/Makefile index 46fd956..37b20c9 100644 --- a/Makefile +++ b/Makefile @@ -40,6 +40,8 @@ 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 "*** To turn off this error, run 'gmake TOOLPREFIX= ...'." 1>&2; \ -- cgit v1.2.3 From 507028de9df94a1e93cdfcd3d1c19fc184d91f06 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 7 Nov 2019 09:46:20 -0500 Subject: more grind --- user/cat.c | 8 +-- user/grind.c | 155 +++++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 145 insertions(+), 18 deletions(-) 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 index a01271e..6203e57 100644 --- a/user/grind.c +++ b/user/grind.c @@ -54,44 +54,56 @@ go(int which_child) 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() % 20; + int what = rand() % 23; if(what == 1){ - close(open("a", O_CREATE|O_RDWR)); + close(open("grindir/../a", O_CREATE|O_RDWR)); } else if(what == 2){ - close(open("b", O_CREATE|O_RDWR)); + close(open("grindir/../grindir/../b", O_CREATE|O_RDWR)); } else if(what == 3){ - unlink("a"); + unlink("grindir/../a"); } else if(what == 4){ - unlink("b"); + if(chdir("grindir") != 0){ + printf("chdir grindir failed\n"); + exit(1); + } + unlink("../b"); + chdir("/"); } else if(what == 5){ close(fd); - fd = open("a", O_CREATE|O_RDWR); + fd = open("/grindir/../a", O_CREATE|O_RDWR); } else if(what == 6){ close(fd); - fd = open("b", O_CREATE|O_RDWR); + 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("a"); - close(open("a/a", O_CREATE|O_RDWR)); + mkdir("grindir/../a"); + close(open("a/../a/./a", O_CREATE|O_RDWR)); unlink("a/a"); } else if(what == 10){ - mkdir("b"); - close(open("b/b", O_CREATE|O_RDWR)); + mkdir("/../b"); + close(open("grindir/../b/b", O_CREATE|O_RDWR)); unlink("b/b"); } else if(what == 11){ unlink("b"); - link("a", "b"); + link("../grindir/./../a", "../b"); } else if(what == 12){ - unlink("a"); - link("b", "a"); + unlink("../grindir/../a"); + link(".././b", "/grindir/../a"); } else if(what == 13){ int pid = fork(); if(pid == 0){ @@ -126,6 +138,10 @@ go(int which_child) 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){ @@ -161,6 +177,117 @@ go(int which_child) 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 > 50){ + 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); + } } } } -- cgit v1.2.3 From c98e1afe7996b639fd36285240e9171b4866fe9d Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 8 Nov 2019 13:21:06 -0500 Subject: allow more files --- user/grind.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user/grind.c b/user/grind.c index 6203e57..14e2aae 100644 --- a/user/grind.c +++ b/user/grind.c @@ -214,7 +214,7 @@ go(int which_child) printf("fstat reports wrong size %d\n", (int)st.size); exit(1); } - if(st.ino > 50){ + if(st.ino > 200){ printf("fstat reports crazy i-number %d\n", st.ino); exit(1); } -- cgit v1.2.3 From f3979b7212f1bb9d72947f54abead5be973c6aed Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 16 Jul 2020 11:38:08 -0400 Subject: make "echo hello > x" truncate file x. --- kernel/defs.h | 1 + kernel/fcntl.h | 1 + kernel/fs.c | 10 ++-- kernel/sysfile.c | 4 ++ user/sh.c | 2 +- user/usertests.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 8 deletions(-) diff --git a/kernel/defs.h b/kernel/defs.h index 9c5f643..f33f1f6 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); 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..e33ec30 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; 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/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/usertests.c b/user/usertests.c index 9aa0ed4..f83a060 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -22,6 +22,141 @@ char buf[BUFSZ]; char name[3]; +// 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) @@ -2169,6 +2304,9 @@ main(int argc, char *argv[]) void (*f)(char *); char *s; } tests[] = { + {truncate1, "truncate1"}, + {truncate2, "truncate2"}, + {truncate3, "truncate3"}, {reparent2, "reparent2"}, {pgbug, "pgbug" }, {sbrkbugs, "sbrkbugs" }, -- cgit v1.2.3 From 7a7cd1adefb6eb29523c8fcb762edfe489639f85 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 17 Jul 2020 16:29:43 -0400 Subject: drop QEMUEXTRA --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 7bfee58..96f1c14 100644 --- a/Makefile +++ b/Makefile @@ -156,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 128M -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 += -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) -- cgit v1.2.3 From d6dad42aaffe7460025f103ed3807f589df6b24d Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 17 Jul 2020 16:29:52 -0400 Subject: rename p->tf to p->trapframe, for consistency with p->context --- kernel/exec.c | 6 +++--- kernel/memlayout.h | 2 +- kernel/proc.c | 18 +++++++++--------- kernel/proc.h | 2 +- kernel/syscall.c | 18 +++++++++--------- kernel/trap.c | 14 +++++++------- 6 files changed, 30 insertions(+), 30 deletions(-) diff --git a/kernel/exec.c b/kernel/exec.c index 6a51499..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,8 +109,8 @@ 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) 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/proc.c b/kernel/proc.c index 0cb5afe..d4ff37c 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -106,7 +106,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; } @@ -129,9 +129,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; @@ -164,7 +164,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; } @@ -206,8 +206,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("/"); @@ -262,10 +262,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++) diff --git a/kernel/proc.h b/kernel/proc.h index 812c769..02e7bc3 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -98,7 +98,7 @@ struct proc { 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 + 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/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/trap.c b/kernel/trap.c index 5e11e4b..77ff868 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. @@ -100,10 +100,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 +115,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); -- cgit v1.2.3 From 3b053f5d58b4914c6389588ad4e556bc887bc99d Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 17 Jul 2020 16:40:57 -0400 Subject: cpu->scheduler -> cpu->context to reduce confusion --- kernel/proc.c | 4 ++-- kernel/proc.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/proc.c b/kernel/proc.c index d4ff37c..f7652f6 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -456,7 +456,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. @@ -490,7 +490,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 02e7bc3..c257eb7 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()? }; -- cgit v1.2.3 From 823864099d0d8def9d4bcf9e382cf42e05bd7afa Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Mon, 20 Jul 2020 06:59:26 -0400 Subject: interrupt-driven uart output, hopefully a nice example for teaching. --- kernel/console.c | 8 +++-- kernel/defs.h | 2 +- kernel/plic.c | 2 -- kernel/trap.c | 8 +++-- kernel/uart.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++------ 5 files changed, 100 insertions(+), 18 deletions(-) diff --git a/kernel/console.c b/kernel/console.c index 87a83ff..a97ef30 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('\b', 0); uartputc(' ', 0); uartputc('\b', 0); } else { - uartputc(c); + uartputc(c, 0); } } @@ -70,7 +72,7 @@ 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, 1); } release(&cons.lock); diff --git a/kernel/defs.h b/kernel/defs.h index f33f1f6..1b164a4 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -149,7 +149,7 @@ void usertrapret(void); // uart.c void uartinit(void); void uartintr(void); -void uartputc(int); +void uartputc(int, int); int uartgetc(void); // vm.c diff --git a/kernel/plic.c b/kernel/plic.c index eed8316..5acba39 100644 --- a/kernel/plic.c +++ b/kernel/plic.c @@ -33,7 +33,6 @@ int plic_claim(void) { int hart = cpuid(); - //int irq = *(uint32*)(PLIC + 0x201004); int irq = *(uint32*)PLIC_SCLAIM(hart); return irq; } @@ -43,6 +42,5 @@ void plic_complete(int irq) { int hart = cpuid(); - //*(uint32*)(PLIC + 0x201004) = irq; *(uint32*)PLIC_SCLAIM(hart) = irq; } diff --git a/kernel/trap.c b/kernel/trap.c index 77ff868..a63249e 100644 --- a/kernel/trap.c +++ b/kernel/trap.c @@ -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 @@ -192,6 +193,9 @@ devintr() printf("unexpected interrupt irq=%d\n", 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); diff --git a/kernel/uart.c b/kernel/uart.c index 3a5cdc4..4d70b42 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -18,7 +18,7 @@ // the UART control registers. // some have different meanings for // read vs write. -// http://byterunner.com/16550.html +// 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 @@ -30,6 +30,15 @@ #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) { @@ -52,18 +61,79 @@ uartinit(void) // reset and enable FIFOs. WriteReg(FCR, 0x07); - // enable receive interrupts. - WriteReg(IER, 0x01); + // enable transmit and receive interrupts. + WriteReg(IER, 0x02 | 0x01); + + initlock(&uart_tx_lock, "uart"); +} + +// add a character to the output buffer and tell the +// UART to start sending if it isn't already. +// +// usually called from the top-half -- by a process +// calling write(). can also be called from a uart +// interrupt to echo a received character, or by printf +// or panic from anywhere in the kernel. +// +// the block argument controls what happens if the +// buffer is full. for write(), block is 1, and the +// process waits. for kernel printf's and echoed +// characters, block is 0, and the character is +// discarded; this is necessary since sleep() is +// not possible in interrupts. +void +uartputc(int c, int block) +{ + acquire(&uart_tx_lock); + while(1){ + if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){ + // buffer is full. + if(block){ + // wait for uartstart() to open up space in the buffer. + sleep(&uart_tx_r, &uart_tx_lock); + } else { + // caller does not want us to wait. + release(&uart_tx_lock); + return; + } + } 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; + } + } } -// write one output character to the UART. +// 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 -uartputc(int c) +uartstart() { - // wait for Transmit Holding Empty to be set in LSR. - while((ReadReg(LSR) & (1 << 5)) == 0) - ; - WriteReg(THR, c); + while(1){ + if(uart_tx_w == uart_tx_r){ + // transmit buffer is empty. + return; + } + + if((ReadReg(LSR) & (1 << 5)) == 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 +149,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); } -- cgit v1.2.3 From db0f092ae44f85db450718588c2deea080c27d0e Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Wed, 22 Jul 2020 10:31:46 -0400 Subject: fix printf() in interrupts --- kernel/console.c | 6 +++--- kernel/defs.h | 3 ++- kernel/uart.c | 45 ++++++++++++++++++++++++--------------------- 3 files changed, 29 insertions(+), 25 deletions(-) diff --git a/kernel/console.c b/kernel/console.c index a97ef30..9a18cd9 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -42,9 +42,9 @@ consputc(int c) if(c == BACKSPACE){ // if the user typed backspace, overwrite with a space. - uartputc('\b', 0); uartputc(' ', 0); uartputc('\b', 0); + uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b'); } else { - uartputc(c, 0); + uartputc_sync(c); } } @@ -72,7 +72,7 @@ consolewrite(int user_src, uint64 src, int n) char c; if(either_copyin(&c, user_src, src+i, 1) == -1) break; - uartputc(c, 1); + uartputc(c); } release(&cons.lock); diff --git a/kernel/defs.h b/kernel/defs.h index 1b164a4..4eedd89 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -149,7 +149,8 @@ void usertrapret(void); // uart.c void uartinit(void); void uartintr(void); -void uartputc(int, int); +void uartputc(int); +void uartputc_sync(int); int uartgetc(void); // vm.c diff --git a/kernel/uart.c b/kernel/uart.c index 4d70b42..32cb575 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -69,33 +69,19 @@ uartinit(void) // add a character to the output buffer and tell the // UART to start sending if it isn't already. -// -// usually called from the top-half -- by a process -// calling write(). can also be called from a uart -// interrupt to echo a received character, or by printf -// or panic from anywhere in the kernel. -// -// the block argument controls what happens if the -// buffer is full. for write(), block is 1, and the -// process waits. for kernel printf's and echoed -// characters, block is 0, and the character is -// discarded; this is necessary since sleep() is -// not possible in interrupts. +// 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, int block) +uartputc(int c) { acquire(&uart_tx_lock); while(1){ if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){ // buffer is full. - if(block){ - // wait for uartstart() to open up space in the buffer. - sleep(&uart_tx_r, &uart_tx_lock); - } else { - // caller does not want us to wait. - release(&uart_tx_lock); - return; - } + // 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; @@ -106,6 +92,23 @@ uartputc(int c, int block) } } +// 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) + ; + 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. -- cgit v1.2.3 From 050a69610afee9884bc3df27215d0d5534743975 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 23 Jul 2020 06:27:20 -0400 Subject: defines for UART register bits --- kernel/uart.c | 34 +++++++++++++++++++++------------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/kernel/uart.c b/kernel/uart.c index 32cb575..daf9f04 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -19,13 +19,21 @@ // some have different meanings for // read vs write. // 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 FCR 2 // FIFO control register -#define ISR 2 // interrupt status register -#define LCR 3 // line control register -#define LSR 5 // line status register +#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)) @@ -46,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); @@ -56,13 +64,13 @@ 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, 0x02 | 0x01); + WriteReg(IER, IER_TX_ENABLE | IER_RX_ENABLE); initlock(&uart_tx_lock, "uart"); } @@ -102,7 +110,7 @@ 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); @@ -122,7 +130,7 @@ uartstart() return; } - if((ReadReg(LSR) & (1 << 5)) == 0){ + 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. -- cgit v1.2.3 From 6c3099d31401dfb76204c276572b7a34caad9b44 Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Thu, 6 Aug 2020 20:30:43 -0400 Subject: Change tf -> trapframe in a few comments --- kernel/trampoline.S | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 -- cgit v1.2.3 From 8b9b7999376f67f45cf9efde675d418841a264ce Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 7 Aug 2020 05:32:48 -0400 Subject: modify each page in usertests countfree() get rid of static for walk() and freewalk() --- kernel/proc.c | 5 +++-- kernel/proc.h | 2 +- kernel/vm.c | 8 +++----- user/usertests.c | 5 ++++- 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/kernel/proc.c b/kernel/proc.c index f7652f6..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) { @@ -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) { diff --git a/kernel/proc.h b/kernel/proc.h index c257eb7..9c16ea7 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -97,7 +97,7 @@ struct proc { // these are private to the process, so p->lock need not be held. uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) - pagetable_t pagetable; // Page table + 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 diff --git a/kernel/vm.c b/kernel/vm.c index 3004bb3..4f65d4e 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..39 -- 9 bits of level-1 index. // 12..20 -- 9 bits of level-0 index. // 0..12 -- 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. diff --git a/user/usertests.c b/user/usertests.c index f83a060..aefbc9f 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -2251,9 +2251,12 @@ countfree() int n = 0; while(1){ - if((uint64)sbrk(4096) == 0xffffffffffffffff){ + 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)); -- cgit v1.2.3 From f01b1a8b71b8e0e8d7564a475a31c4e951463874 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 7 Aug 2020 14:34:39 -0400 Subject: test pointer checking in copyin, copyout, copyinstr --- user/usertests.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/user/usertests.c b/user/usertests.c index aefbc9f..bdf6970 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -22,6 +22,92 @@ char buf[BUFSZ]; char name[3]; +void +copyin1(char *s) +{ + int fd = open("copyin1", O_CREATE|O_WRONLY); + if(fd < 0){ + printf("open(copyin1) failed\n"); + exit(1); + } + int n = write(fd, (void*)0x80000000LL, 8192); + if(n >= 0){ + printf("write(fd, 0x80000000LL, 8192) did not fail!\n"); + exit(1); + } + close(fd); + unlink("copyin1"); +} + +void +copyin2(char *s) +{ + int fd = open("copyin2", O_CREATE|O_WRONLY); + if(fd < 0){ + printf("open(copyin2) failed\n"); + exit(1); + } + int n = write(fd, (void*)0xffffffffffffffffLL, 8192); + if(n >= 0){ + printf("write(fd, 0xffffffffffffffffLL, 8192) did not fail!\n"); + exit(1); + } + close(fd); + unlink("copyin2"); +} + +void +copyout1(char *s) +{ + int fd = open("README", 0); + if(fd < 0){ + printf("open(README) failed\n"); + exit(1); + } + int n = read(fd, (void*)0x80000000LL, 8192); + if(n >= 0){ + printf("read(fd, 0x80000000LL, 8192) returned %d, not -1\n", n); + exit(1); + } + close(fd); +} + +void +copyout2(char *s) +{ + int fd = open("README", 0); + if(fd < 0){ + printf("open(README) failed\n"); + exit(1); + } + int n = read(fd, (void*)0xffffffffffffffffLL, 8192); + if(n >= 0){ + printf("read(fd, 0xffffffffffffffff, 8192) returned %d, not -1\n", n); + exit(1); + } + close(fd); +} + +void +copyinstr1(char *s) +{ + int fd = open((char *)0x80000000LL, O_CREATE|O_WRONLY); + if(fd >= 0){ + printf("open(0x80000000) returned %d, not -1\n", fd); + exit(1); + } +} + +void +copyinstr2(char *s) +{ + int fd = open((char *)0xffffffffffffffff, O_CREATE|O_WRONLY); + if(fd >= 0){ + printf("open(0xffffffffffffffff) returned %d, not -1\n", fd); + exit(1); + } +} + // test O_TRUNC. void truncate1(char *s) @@ -2307,6 +2393,12 @@ main(int argc, char *argv[]) void (*f)(char *); char *s; } tests[] = { + {copyin1, "copyin1"}, + {copyin2, "copyin2"}, + {copyout1, "copyout1"}, + {copyout2, "copyout2"}, + {copyinstr1, "copyinstr1"}, + {copyinstr2, "copyinstr2"}, {truncate1, "truncate1"}, {truncate2, "truncate2"}, {truncate3, "truncate3"}, -- cgit v1.2.3 From 354adfdafc3993771f58236771e213016ff9aed8 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 7 Aug 2020 15:06:43 -0400 Subject: streamline copyin/copyout code in usertests fix bugs in read/write return values when there's an error --- kernel/console.c | 2 +- kernel/fs.c | 2 +- kernel/pipe.c | 2 +- user/usertests.c | 157 +++++++++++++++++++++++++++++-------------------------- 4 files changed, 86 insertions(+), 77 deletions(-) diff --git a/kernel/console.c b/kernel/console.c index 9a18cd9..2b1ed3c 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -76,7 +76,7 @@ consolewrite(int user_src, uint64 src, int n) } release(&cons.lock); - return n; + return i; } // diff --git a/kernel/fs.c b/kernel/fs.c index e33ec30..ec68cd7 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -472,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/pipe.c b/kernel/pipe.c index c066afb..7ed402d 100644 --- a/kernel/pipe.c +++ b/kernel/pipe.c @@ -96,7 +96,7 @@ pipewrite(struct pipe *pi, uint64 addr, int n) } wakeup(&pi->nread); release(&pi->lock); - return n; + return i; } int diff --git a/user/usertests.c b/user/usertests.c index bdf6970..8eb4aab 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -23,88 +23,100 @@ char buf[BUFSZ]; char name[3]; void -copyin1(char *s) +copyin(char *s) { - int fd = open("copyin1", O_CREATE|O_WRONLY); - if(fd < 0){ - printf("open(copyin1) failed\n"); - exit(1); - } - int n = write(fd, (void*)0x80000000LL, 8192); - if(n >= 0){ - printf("write(fd, 0x80000000LL, 8192) did not fail!\n"); - exit(1); - } - close(fd); - unlink("copyin1"); -} + uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff }; -void -copyin2(char *s) -{ - int fd = open("copyin2", O_CREATE|O_WRONLY); - if(fd < 0){ - printf("open(copyin2) failed\n"); - exit(1); - } - int n = write(fd, (void*)0xffffffffffffffffLL, 8192); - if(n >= 0){ - printf("write(fd, 0xffffffffffffffffLL, 8192) did not fail!\n"); - exit(1); + 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]); } - close(fd); - unlink("copyin2"); } void -copyout1(char *s) +copyout(char *s) { - int fd = open("README", 0); - if(fd < 0){ - printf("open(README) failed\n"); - exit(1); - } - int n = read(fd, (void*)0x80000000LL, 8192); - if(n >= 0){ - printf("read(fd, 0x80000000LL, 8192) returned %d, not -1\n", n); - exit(1); - } - close(fd); -} + uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff }; -void -copyout2(char *s) -{ - int fd = open("README", 0); - if(fd < 0){ - printf("open(README) failed\n"); - exit(1); - } - int n = read(fd, (void*)0xffffffffffffffffLL, 8192); - if(n >= 0){ - printf("read(fd, 0xffffffffffffffff, 8192) returned %d, not -1\n", n); - exit(1); - } - close(fd); -} + for(int ai = 0; ai < 2; ai++){ + uint64 addr = addrs[ai]; -void -copyinstr1(char *s) -{ - int fd = open((char *)0x80000000LL, O_CREATE|O_WRONLY); - if(fd >= 0){ - printf("open(0x80000000) returned %d, not -1\n", fd); - exit(1); + 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]); } } void -copyinstr2(char *s) +copyinstr(char *s) { - int fd = open((char *)0xffffffffffffffff, O_CREATE|O_WRONLY); - if(fd >= 0){ - printf("open(0xffffffffffffffff) returned %d, not -1\n", fd); - exit(1); + 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); + } } } @@ -2393,12 +2405,9 @@ main(int argc, char *argv[]) void (*f)(char *); char *s; } tests[] = { - {copyin1, "copyin1"}, - {copyin2, "copyin2"}, - {copyout1, "copyout1"}, - {copyout2, "copyout2"}, - {copyinstr1, "copyinstr1"}, - {copyinstr2, "copyinstr2"}, + {copyin, "copyin"}, + {copyout, "copyout"}, + {copyinstr, "copyinstr"}, {truncate1, "truncate1"}, {truncate2, "truncate2"}, {truncate3, "truncate3"}, -- cgit v1.2.3 From 234391b6bf4d4809930300c1637403987c273461 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 7 Aug 2020 16:39:56 -0400 Subject: test copyinstr()'s handling of the terminating null --- user/usertests.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 2 deletions(-) diff --git a/user/usertests.c b/user/usertests.c index 8eb4aab..5c2fc02 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -105,7 +105,7 @@ copyout(char *s) } void -copyinstr(char *s) +copyinstr1(char *s) { uint64 addrs[] = { 0x80000000LL, 0xffffffffffffffff }; @@ -120,6 +120,67 @@ copyinstr(char *s) } } +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); + } +} + // test O_TRUNC. void truncate1(char *s) @@ -2407,7 +2468,8 @@ main(int argc, char *argv[]) } tests[] = { {copyin, "copyin"}, {copyout, "copyout"}, - {copyinstr, "copyinstr"}, + {copyinstr1, "copyinstr1"}, + {copyinstr2, "copyinstr2"}, {truncate1, "truncate1"}, {truncate2, "truncate2"}, {truncate3, "truncate3"}, -- cgit v1.2.3 From 6cb6764bb1778b8c4f92680295f928fec074e3d7 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 7 Aug 2020 16:56:00 -0400 Subject: test string system call arguments that cross over the end of the last page. --- user/usertests.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/user/usertests.c b/user/usertests.c index 5c2fc02..dfe0039 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -22,6 +22,8 @@ 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) { @@ -64,6 +66,8 @@ copyin(char *s) } } +// what if you pass ridiculous pointers to system calls +// that write user memory with copyout? void copyout(char *s) { @@ -104,6 +108,7 @@ copyout(char *s) } } +// what if you pass ridiculous string pointers to system calls? void copyinstr1(char *s) { @@ -120,6 +125,9 @@ copyinstr1(char *s) } } +// 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) { @@ -181,6 +189,50 @@ copyinstr2(char *s) } } +// 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) @@ -2470,6 +2522,7 @@ main(int argc, char *argv[]) {copyout, "copyout"}, {copyinstr1, "copyinstr1"}, {copyinstr2, "copyinstr2"}, + {copyinstr3, "copyinstr3"}, {truncate1, "truncate1"}, {truncate2, "truncate2"}, {truncate3, "truncate3"}, -- cgit v1.2.3 From ffcb883adff508497f71fae6ca72a8fa70483edc Mon Sep 17 00:00:00 2001 From: Fumiya Shigemitsu Date: Mon, 21 Oct 2019 21:01:07 +0900 Subject: Fix minor typos --- kernel/vm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/vm.c b/kernel/vm.c index 4f65d4e..636f11a 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -65,9 +65,9 @@ kvminithart() // A 64-bit virtual address is split into five fields: // 39..63 -- must be zero. // 30..38 -- 9 bits of level-2 index. -// 21..39 -- 9 bits of level-1 index. +// 21..29 -- 9 bits of level-1 index. // 12..20 -- 9 bits of level-0 index. -// 0..12 -- 12 bits of byte offset within the page. +// 0..11 -- 12 bits of byte offset within the page. pte_t * walk(pagetable_t pagetable, uint64 va, int alloc) { -- cgit v1.2.3 From b557e7c32e935b9bb2f5d8ed8503de52f43cf87f Mon Sep 17 00:00:00 2001 From: Jonathan Kimmitt Date: Thu, 16 Jan 2020 15:05:27 +0000 Subject: Update ramdisk.c The qemu syntax for a ram disk was documented incorrectly. The documented syntax is here: https://qemu.weilnetz.de/doc/qemu-doc.html --- kernel/ramdisk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/ramdisk.c b/kernel/ramdisk.c index 9901294..eb60ee7 100644 --- a/kernel/ramdisk.c +++ b/kernel/ramdisk.c @@ -1,5 +1,5 @@ // -// ramdisk that uses the disk image loaded by qemu -rdinit fs.img +// ramdisk that uses the disk image loaded by qemu -initrd fs.img // #include "types.h" -- cgit v1.2.3 From 9eff4b0871ca68376bc409c991fbce414e700921 Mon Sep 17 00:00:00 2001 From: Takahiro Date: Sat, 18 Apr 2020 14:49:54 -0700 Subject: Write interrupt ack register in virtio_disk_intr() --- kernel/virtio_disk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/virtio_disk.c b/kernel/virtio_disk.c index 3cff024..06e0645 100644 --- a/kernel/virtio_disk.c +++ b/kernel/virtio_disk.c @@ -264,6 +264,7 @@ virtio_disk_intr() disk.used_idx = (disk.used_idx + 1) % NUM; } + *R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3; release(&disk.vdisk_lock); } -- cgit v1.2.3 From c24844714bf68feb8965c16be6ad9c82e27cc530 Mon Sep 17 00:00:00 2001 From: Asami Doi Date: Fri, 20 Mar 2020 12:33:32 +0900 Subject: update initcode to avoid using the compressed extension --- kernel/proc.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/proc.c b/kernel/proc.c index cf7f260..417e30a 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -183,13 +183,13 @@ proc_freepagetable(pagetable_t pagetable, uint64 sz) // a user program that calls exec("/init") // od -t xC initcode uchar initcode[] = { - 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x05, 0x02, - 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x05, 0x02, - 0x9d, 0x48, 0x73, 0x00, 0x00, 0x00, 0x89, 0x48, - 0x73, 0x00, 0x00, 0x00, 0xef, 0xf0, 0xbf, 0xff, - 0x2f, 0x69, 0x6e, 0x69, 0x74, 0x00, 0x00, 0x01, - 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00 + 0x17, 0x05, 0x00, 0x00, 0x13, 0x05, 0x45, 0x02, + 0x97, 0x05, 0x00, 0x00, 0x93, 0x85, 0x35, 0x02, + 0x93, 0x08, 0x70, 0x00, 0x73, 0x00, 0x00, 0x00, + 0x93, 0x08, 0x20, 0x00, 0x73, 0x00, 0x00, 0x00, + 0xef, 0xf0, 0x9f, 0xff, 0x2f, 0x69, 0x6e, 0x69, + 0x74, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 }; // Set up first user process. -- cgit v1.2.3 From f14aa421c456875fbb5fff0ef2f5c4154f11e38d Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Mon, 10 Aug 2020 13:55:26 -0400 Subject: Generate initcode without compressed extensions --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 96f1c14..a85add2 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,7 @@ $K/kernel: $(OBJS) $K/kernel.ld $U/initcode $(OBJDUMP) -t $K/kernel | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$$/d' > $K/kernel.sym $U/initcode: $U/initcode.S - $(CC) $(CFLAGS) -nostdinc -I. -Ikernel -c $U/initcode.S -o $U/initcode.o + $(CC) $(CFLAGS) -march=rv64g -nostdinc -I. -Ikernel -c $U/initcode.S -o $U/initcode.o $(LD) $(LDFLAGS) -N -e start -Ttext 0 -o $U/initcode.out $U/initcode.o $(OBJCOPY) -S -O binary $U/initcode.out $U/initcode $(OBJDUMP) -S $U/initcode.o > $U/initcode.asm -- cgit v1.2.3 From 2db95f08b14f2779a31cb9e3e830f762f36f2660 Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Mon, 10 Aug 2020 16:25:51 -0400 Subject: Typo (thanks yt ) --- kernel/entry.S | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/entry.S b/kernel/entry.S index ef5a56a..62fe1f2 100644 --- a/kernel/entry.S +++ b/kernel/entry.S @@ -1,7 +1,7 @@ # qemu -kernel starts at 0x1000. the instructions # there seem to be provided by qemu, as if it # were a ROM. the code at 0x1000 jumps to - # 0x8000000, the _start function here, + # 0x80000000, the _start function here, # in machine mode. each CPU starts here. .section .data .globl stack0 -- cgit v1.2.3 From 737bd3a55d9380906f097f1d82d47fe2b23bf486 Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Mon, 10 Aug 2020 16:42:33 -0400 Subject: Ack bug finders. --- README | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README b/README index 87a3833..38e6f5f 100644 --- a/README +++ b/README @@ -15,15 +15,17 @@ Clements. We are also grateful for the bug reports and patches contributed by Silas Boyd-Wickizer, Anton Burtsev, Dan Cross, Cody Cutler, Mike CAT, -Tej Chajed, eyalz800, Nelson Elhage, Saar Ettinger, Alice Ferrazzi, -Nathaniel Filardo, Peter Froehlich, Yakir Goaron,Shivam Handa, Bryan -Henry, Jim Huang, Alexander Kapshuk, Anders Kaseorg, kehao95, Wolfgang -Keller, Eddie Kohler, Austin Liew, Imbar Marinescu, Yandong Mao, Matan -Shabtay, Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel -Nider, Greg Price, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Cam -Tenny, tyfkda, Rafael Ubal, Warren Toomey, Stephen Tu, Pablo Ventura, -Xi Wang, Keiichi Watanabe, Nicolas Wolovick, wxdao, Grant Wu, Jindong -Zhang, Icenowy Zheng, and Zou Chang Wei. +Tej Chajed, Asami Doi, eyalz800, , Nelson Elhage, Saar Ettinger, Alice +Ferrazzi, Nathaniel Filardo, Peter Froehlich, Yakir Goaron,Shivam +Handa, Bryan Henry, jaichenhengjie, Jim Huang, Alexander Kapshuk, +Anders Kaseorg, kehao95, Wolfgang Keller, Jonathan Kimmitt, Eddie +Kohler, Austin Liew, Imbar Marinescu, Yandong Mao, Matan Shabtay, +Hitoshi Mitake, Carmi Merimovich, Mark Morrissey, mtasm, Joel Nider, +Greg Price, Ayan Shafqat, Eldar Sehayek, Yongming Shen, Fumiya +Shigemitsu, Takahiro, Cam Tenny, tyfkda, Rafael Ubal, Warren Toomey, +Stephen Tu, Pablo Ventura, Xi Wang, Keiichi Watanabe, Nicolas +Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng, and Zou Chang +Wei. The code in the files that constitute xv6 is Copyright 2006-2019 Frans Kaashoek, Robert Morris, and Russ Cox. -- cgit v1.2.3 From 468946781fd551d69252f13a41ffc77c5e30fae9 Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Mon, 10 Aug 2020 19:41:57 -0400 Subject: copyright and 6.S081 --- README | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README b/README index 38e6f5f..06035bb 100644 --- a/README +++ b/README @@ -28,13 +28,13 @@ Wolovick, wxdao, Grant Wu, Jindong Zhang, Icenowy Zheng, and Zou Chang Wei. The code in the files that constitute xv6 is -Copyright 2006-2019 Frans Kaashoek, Robert Morris, and Russ Cox. +Copyright 2006-2020 Frans Kaashoek, Robert Morris, and Russ Cox. ERROR REPORTS Please send errors and suggestions to Frans Kaashoek and Robert Morris (kaashoek,rtm@mit.edu). The main purpose of xv6 is as a teaching -operating system for MIT's 6.828, so we are more interested in +operating system for MIT's 6.S081, so we are more interested in simplifications and clarifications than new features. BUILDING AND RUNNING XV6 -- cgit v1.2.3 From 315c37c0f1e7c05f7b7d0ec145a2002b0612c49c Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Mon, 10 Aug 2020 20:28:12 -0400 Subject: Update to the latest specs --- doc/riscv-privileged-20190608-1.pdf | Bin 0 -> 533858 bytes doc/riscv-privileged-v1.10.pdf | Bin 536816 -> 0 bytes doc/riscv-spec-20191213.pdf | Bin 0 -> 1021610 bytes doc/riscv-spec-v2.2.pdf | Bin 615016 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/riscv-privileged-20190608-1.pdf delete mode 100644 doc/riscv-privileged-v1.10.pdf create mode 100644 doc/riscv-spec-20191213.pdf delete mode 100644 doc/riscv-spec-v2.2.pdf diff --git a/doc/riscv-privileged-20190608-1.pdf b/doc/riscv-privileged-20190608-1.pdf new file mode 100644 index 0000000..2303a01 Binary files /dev/null and b/doc/riscv-privileged-20190608-1.pdf differ diff --git a/doc/riscv-privileged-v1.10.pdf b/doc/riscv-privileged-v1.10.pdf deleted file mode 100644 index 6942fe7..0000000 Binary files a/doc/riscv-privileged-v1.10.pdf and /dev/null differ diff --git a/doc/riscv-spec-20191213.pdf b/doc/riscv-spec-20191213.pdf new file mode 100644 index 0000000..533c1cb Binary files /dev/null and b/doc/riscv-spec-20191213.pdf differ diff --git a/doc/riscv-spec-v2.2.pdf b/doc/riscv-spec-v2.2.pdf deleted file mode 100644 index e4a4634..0000000 Binary files a/doc/riscv-spec-v2.2.pdf and /dev/null differ -- cgit v1.2.3 From d32eac366fd06ec58cc3b34a3ca1824864bd7618 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 13 Aug 2020 08:04:56 -0400 Subject: pa isn't needed in the uvmunmap() loop --- kernel/vm.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/kernel/vm.c b/kernel/vm.c index 4f65d4e..d45210a 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -175,7 +175,6 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 size, int do_free) { uint64 a, last; pte_t *pte; - uint64 pa; a = PGROUNDDOWN(va); last = PGROUNDDOWN(va + size - 1); @@ -189,14 +188,13 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 size, int do_free) if(PTE_FLAGS(*pte) == PTE_V) panic("uvmunmap: not a leaf"); if(do_free){ - pa = PTE2PA(*pte); + uint64 pa = PTE2PA(*pte); kfree((void*)pa); } *pte = 0; if(a == last) break; a += PGSIZE; - pa += PGSIZE; } } -- cgit v1.2.3 From 70c6fe861e64195032083a9fd59a81bd113d1b0b Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 13 Aug 2020 08:46:28 -0400 Subject: modify uvmunmap() to be in aligned pages fix a bug in fork() recovering from out of memory --- kernel/proc.c | 4 ++-- kernel/vm.c | 35 ++++++++++++++++------------------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/kernel/proc.c b/kernel/proc.c index 417e30a..1774add 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -175,8 +175,8 @@ proc_pagetable(struct proc *p) void proc_freepagetable(pagetable_t pagetable, uint64 sz) { - uvmunmap(pagetable, TRAMPOLINE, PGSIZE, 0); - uvmunmap(pagetable, TRAPFRAME, PGSIZE, 0); + uvmunmap(pagetable, TRAMPOLINE, 1, 0); + uvmunmap(pagetable, TRAPFRAME, 1, 0); uvmfree(pagetable, sz); } diff --git a/kernel/vm.c b/kernel/vm.c index b48a022..92a5ff7 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -167,24 +167,23 @@ mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) return 0; } -// Remove mappings from a page table. The mappings in -// the given range must exist. Optionally free the -// physical memory. +// Remove npages of mappings starting from va. va must be +// page-aligned. The mappings must exist. +// Optionally free the physical memory. void -uvmunmap(pagetable_t pagetable, uint64 va, uint64 size, int do_free) +uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) { - uint64 a, last; + uint64 a; pte_t *pte; - a = PGROUNDDOWN(va); - last = PGROUNDDOWN(va + size - 1); - for(;;){ + if((va % PGSIZE) != 0) + panic("uvmunmap: not aligned"); + + for(a = va; a < va + npages*PGSIZE; a += PGSIZE){ if((pte = walk(pagetable, a, 0)) == 0) panic("uvmunmap: walk"); - if((*pte & PTE_V) == 0){ - printf("va=%p pte=%p\n", a, *pte); + if((*pte & PTE_V) == 0) panic("uvmunmap: not mapped"); - } if(PTE_FLAGS(*pte) == PTE_V) panic("uvmunmap: not a leaf"); if(do_free){ @@ -192,9 +191,6 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 size, int do_free) kfree((void*)pa); } *pte = 0; - if(a == last) - break; - a += PGSIZE; } } @@ -265,9 +261,10 @@ uvmdealloc(pagetable_t pagetable, uint64 oldsz, uint64 newsz) if(newsz >= oldsz) return oldsz; - uint64 newup = PGROUNDUP(newsz); - if(newup < PGROUNDUP(oldsz)) - uvmunmap(pagetable, newup, oldsz - newup, 1); + if(PGROUNDUP(newsz) < PGROUNDUP(oldsz)){ + int npages = (PGROUNDUP(oldsz) - PGROUNDUP(newsz)) / PGSIZE; + uvmunmap(pagetable, PGROUNDUP(newsz), npages, 1); + } return newsz; } @@ -298,7 +295,7 @@ void uvmfree(pagetable_t pagetable, uint64 sz) { if(sz > 0) - uvmunmap(pagetable, 0, sz, 1); + uvmunmap(pagetable, 0, PGROUNDUP(sz)/PGSIZE, 1); freewalk(pagetable); } @@ -334,7 +331,7 @@ uvmcopy(pagetable_t old, pagetable_t new, uint64 sz) return 0; err: - uvmunmap(new, 0, i, 1); + uvmunmap(new, 0, i / PGSIZE, 1); return -1; } -- cgit v1.2.3 From 4c22c54480f020c36de120ce868912c022e48113 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 13 Aug 2020 09:19:23 -0400 Subject: try to handle a few of the possible out-of-memory errors in fork() --- kernel/proc.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/kernel/proc.c b/kernel/proc.c index 1774add..2811142 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -17,6 +17,7 @@ struct spinlock pid_lock; extern void forkret(void); static void wakeup1(struct proc *chan); +static void freeproc(struct proc *p); extern char trampoline[]; // trampoline.S @@ -87,7 +88,7 @@ allocpid() { // Look in the process table for an UNUSED proc. // If found, initialize state required to run in the kernel, // and return with p->lock held. -// If there are no free procs, return 0. +// If there are no free procs, or a memory allocation fails, return 0. static struct proc* allocproc(void) { @@ -114,6 +115,11 @@ found: // An empty user page table. p->pagetable = proc_pagetable(p); + if(p->pagetable == 0){ + freeproc(p); + release(&p->lock); + return 0; + } // Set up new context to start executing at forkret, // which returns to user space. @@ -160,12 +166,19 @@ proc_pagetable(struct proc *p) // at the highest user virtual address. // only the supervisor uses it, on the way // to/from user space, so not PTE_U. - mappages(pagetable, TRAMPOLINE, PGSIZE, - (uint64)trampoline, PTE_R | PTE_X); + if(mappages(pagetable, TRAMPOLINE, PGSIZE, + (uint64)trampoline, PTE_R | PTE_X) < 0){ + uvmfree(pagetable, 0); + return 0; + } // map the trapframe just below TRAMPOLINE, for trampoline.S. - mappages(pagetable, TRAPFRAME, PGSIZE, - (uint64)(p->trapframe), PTE_R | PTE_W); + if(mappages(pagetable, TRAPFRAME, PGSIZE, + (uint64)(p->trapframe), PTE_R | PTE_W) < 0){ + uvmunmap(pagetable, TRAMPOLINE, 1, 0); + uvmfree(pagetable, 0); + return 0; + } return pagetable; } -- cgit v1.2.3 From adee82c3e7334a09996c0fe9cc75d9a80abc81c8 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 13 Aug 2020 10:22:07 -0400 Subject: handle another out-of-memory in fork(). the policy here is not consistent, since other calls (e.g. exec()) panic on out of memory. --- kernel/proc.c | 2 ++ kernel/vm.c | 3 ++- user/usertests.c | 8 ++++++-- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/kernel/proc.c b/kernel/proc.c index 2811142..56314e5 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -161,6 +161,8 @@ proc_pagetable(struct proc *p) // An empty page table. pagetable = uvmcreate(); + if(pagetable == 0) + return 0; // map the trampoline code (for system call return) // at the highest user virtual address. diff --git a/kernel/vm.c b/kernel/vm.c index 92a5ff7..2f3789f 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -195,13 +195,14 @@ uvmunmap(pagetable_t pagetable, uint64 va, uint64 npages, int do_free) } // create an empty user page table. +// returns 0 if out of memory. pagetable_t uvmcreate() { pagetable_t pagetable; pagetable = (pagetable_t) kalloc(); if(pagetable == 0) - panic("uvmcreate: out of memory"); + return 0; memset(pagetable, 0, PGSIZE); return pagetable; } diff --git a/user/usertests.c b/user/usertests.c index dfe0039..cc88555 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -2507,6 +2507,8 @@ main(int argc, char *argv[]) if(argc == 2 && strcmp(argv[1], "-c") == 0){ continuous = 1; + } else if(argc == 2 && strcmp(argv[1], "-C") == 0){ + continuous = 2; } else if(argc == 2 && argv[1][0] != '-'){ justone = argv[1]; } else if(argc > 1){ @@ -2589,12 +2591,14 @@ main(int argc, char *argv[]) } if(fail){ printf("SOME TESTS FAILED\n"); - exit(1); + if(continuous != 2) + exit(1); } int free1 = countfree(); if(free1 < free0){ printf("FAILED -- lost some free pages\n"); - exit(1); + if(continuous != 2) + exit(1); } } } -- cgit v1.2.3 From 4a87a0ae8bc4a186842fb5f57454412de56f52bc Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 13 Aug 2020 14:10:58 -0400 Subject: fix a bug in the out-of-memory handling code in exec --- kernel/exec.c | 7 ++++--- kernel/vm.c | 3 +-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/kernel/exec.c b/kernel/exec.c index 1077ac0..8a2d1dc 100644 --- a/kernel/exec.c +++ b/kernel/exec.c @@ -14,7 +14,7 @@ exec(char *path, char **argv) { char *s, *last; int i, off; - uint64 argc, sz, sp, ustack[MAXARG+1], stackbase; + uint64 argc, sz = 0, sp, ustack[MAXARG+1], stackbase; struct elfhdr elf; struct inode *ip; struct proghdr ph; @@ -39,7 +39,6 @@ exec(char *path, char **argv) goto bad; // Load program into memory. - sz = 0; for(i=0, off=elf.phoff; i Date: Sat, 15 Aug 2020 05:46:32 -0400 Subject: x --- kernel/entry.S | 18 ++++++------------ user/init.c | 16 ++++++++++++++-- user/initcode.S | 2 +- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/kernel/entry.S b/kernel/entry.S index 62fe1f2..b72ddbc 100644 --- a/kernel/entry.S +++ b/kernel/entry.S @@ -1,14 +1,8 @@ - # qemu -kernel starts at 0x1000. the instructions - # there seem to be provided by qemu, as if it - # were a ROM. the code at 0x1000 jumps to - # 0x80000000, the _start function here, - # in machine mode. each CPU starts here. -.section .data -.globl stack0 + # qemu -kernel loads the kernel at 0x80000000 + # and causes each CPU to jump there. + # kernel.ld causes the following code to + # be placed at 0x80000000. .section .text -.globl start -.section .text -.globl _entry _entry: # set up a stack for C. # stack0 is declared in start.c, @@ -22,5 +16,5 @@ _entry: add sp, sp, a0 # jump to start() in start.c call start -junk: - j junk +spin: + j spin diff --git a/user/init.c b/user/init.c index 5df6deb..13764ca 100644 --- a/user/init.c +++ b/user/init.c @@ -31,8 +31,20 @@ main(void) printf("init: exec sh failed\n"); exit(1); } - while((wpid=wait(0)) >= 0 && wpid != pid){ - //printf("zombie!\n"); + + for(;;){ + // this call to wait() returns if the shell exits, + // or if a parentless process exits. + wpid = wait((int *) 0); + if(wpid == pid){ + // the shell exited; restart it. + break; + } else if(wpid < 0){ + printf("init: wait returned an error\n"); + exit(1); + } else { + // it was a parentless process; do nothing. + } } } } diff --git a/user/initcode.S b/user/initcode.S index ca76972..e8f7a91 100644 --- a/user/initcode.S +++ b/user/initcode.S @@ -1,4 +1,4 @@ -# Initial process execs /init. +# Initial process that execs /init. # This code runs in user space. #include "syscall.h" -- cgit v1.2.3 From 740d36373630e519117077f867437c9abbfd1f8d Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Mon, 17 Aug 2020 14:25:12 -0400 Subject: Delete some obselete stuff --- .cvsignore | 16 ---------------- Makefile | 43 ------------------------------------------- 2 files changed, 59 deletions(-) delete mode 100644 .cvsignore diff --git a/.cvsignore b/.cvsignore deleted file mode 100644 index 081a43c..0000000 --- a/.cvsignore +++ /dev/null @@ -1,16 +0,0 @@ -*.asm -*.d -*.sym -_* -kernel -user1 -userfs -usertests -xv6.img -vectors.S -bochsout.txt -bootblock -bootother -bootother.out -parport.out -fmt diff --git a/Makefile b/Makefile index a85add2..328f9c6 100644 --- a/Makefile +++ b/Makefile @@ -170,46 +170,3 @@ qemu-gdb: $K/kernel .gdbinit fs.img @echo "*** Now run 'gdb' in another window." 1>&2 $(QEMU) $(QEMUOPTS) -S $(QEMUGDB) -# CUT HERE -# prepare dist for students -# after running make dist, probably want to -# rename it to rev0 or rev1 or so on and then -# check in that version. - -EXTRA=\ - mkfs.c ulib.c user.h cat.c echo.c forktest.c grep.c kill.c\ - ln.c ls.c mkdir.c rm.c stressfs.c usertests.c wc.c zombie.c\ - printf.c umalloc.c\ - README dot-bochsrc *.pl \ - .gdbinit.tmpl gdbutil\ - -dist: - rm -rf dist - mkdir dist - for i in $(FILES); \ - do \ - grep -v PAGEBREAK $$i >dist/$$i; \ - done - sed '/CUT HERE/,$$d' Makefile >dist/Makefile - echo >dist/runoff.spec - cp $(EXTRA) dist - -dist-test: - rm -rf dist - make dist - rm -rf dist-test - mkdir dist-test - cp dist/* dist-test - cd dist-test; $(MAKE) print - cd dist-test; $(MAKE) bochs || true - cd dist-test; $(MAKE) qemu - -# update this rule (change rev#) when it is time to -# make a new revision. -tar: - rm -rf /tmp/xv6 - mkdir -p /tmp/xv6 - cp dist/* dist/.gdbinit.tmpl /tmp/xv6 - (cd /tmp; tar cf - xv6) | gzip >xv6-rev10.tar.gz # the next one will be 10 (9/17) - -.PHONY: dist-test dist -- cgit v1.2.3 From b33574df38f358ba2173caa92e8287b5b0cb9cff Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Tue, 18 Aug 2020 20:48:53 -0400 Subject: Use the major number defined in file.h. The minor number is ignored; might as well use 0. --- user/init.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/user/init.c b/user/init.c index 13764ca..e0a5689 100644 --- a/user/init.c +++ b/user/init.c @@ -2,6 +2,10 @@ #include "kernel/types.h" #include "kernel/stat.h" +#include "kernel/spinlock.h" +#include "kernel/sleeplock.h" +#include "kernel/fs.h" +#include "kernel/file.h" #include "user/user.h" #include "kernel/fcntl.h" @@ -13,7 +17,7 @@ main(void) int pid, wpid; if(open("console", O_RDWR) < 0){ - mknod("console", 1, 1); + mknod("console", CONSOLE, 0); open("console", O_RDWR); } dup(0); // stdout -- cgit v1.2.3 From aefa2697d705e316aa5255004e4b6a129e9afe2a Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Wed, 19 Aug 2020 12:35:14 -0400 Subject: usertest for exec() out of memory recovery and fix a few exec() bugs --- kernel/exec.c | 4 +++- kernel/sysfile.c | 5 ++--- user/usertests.c | 37 +++++++++++++++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 4 deletions(-) diff --git a/kernel/exec.c b/kernel/exec.c index 8a2d1dc..0e8762f 100644 --- a/kernel/exec.c +++ b/kernel/exec.c @@ -67,8 +67,10 @@ exec(char *path, char **argv) // Allocate two pages at the next page boundary. // Use the second as the user stack. sz = PGROUNDUP(sz); - if((sz = uvmalloc(pagetable, sz, sz + 2*PGSIZE)) == 0) + uint64 sz1; + if((sz1 = uvmalloc(pagetable, sz, sz + 2*PGSIZE)) == 0) goto bad; + sz = sz1; uvmclear(pagetable, sz-2*PGSIZE); sp = sz; stackbase = sp - PGSIZE; diff --git a/kernel/sysfile.c b/kernel/sysfile.c index 015c942..5dc453b 100644 --- a/kernel/sysfile.c +++ b/kernel/sysfile.c @@ -436,10 +436,9 @@ sys_exec(void) } argv[i] = kalloc(); if(argv[i] == 0) - panic("sys_exec kalloc"); - if(fetchstr(uarg, argv[i], PGSIZE) < 0){ goto bad; - } + if(fetchstr(uarg, argv[i], PGSIZE) < 0) + goto bad; } int ret = exec(path, argv); diff --git a/user/usertests.c b/user/usertests.c index cc88555..acdba0f 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -2452,6 +2452,42 @@ badarg(char *s) exit(0); } +// test the exec() code that cleans up if it runs out +// of memory. it's really a test that such a condition +// doesn't cause a panic. +void +execout(char *s) +{ + for(int avail = 0; avail < 15; avail++){ + int pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + exit(1); + } else if(pid == 0){ + // allocate all of memory. + while(1){ + uint64 a = (uint64) sbrk(4096); + if(a == 0xffffffffffffffffLL) + break; + } + + // free a few pages, in order to let exec() make some + // progress. + for(int i = 0; i < avail; i++) + sbrk(-4096); + + close(1); + char *args[] = { "echo", "x", 0 }; + exec("echo", args); + exit(0); + } else { + wait((int*)0); + } + } + + exit(0); +} + // // use sbrk() to count how many free physical memory pages there are. // @@ -2520,6 +2556,7 @@ main(int argc, char *argv[]) void (*f)(char *); char *s; } tests[] = { + {execout, "execout"}, {copyin, "copyin"}, {copyout, "copyout"}, {copyinstr1, "copyinstr1"}, -- cgit v1.2.3 From 59a9863a17d881498bf7519878566f74341a1b71 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Wed, 19 Aug 2020 13:10:14 -0400 Subject: touch sbrk()-allocated memory to make sure it exists --- user/usertests.c | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/user/usertests.c b/user/usertests.c index acdba0f..502621a 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -2006,6 +2006,12 @@ sbrkmuch(char *s) printf("%s: sbrk test failed to grow big address space; enough phys mem?\n", s); exit(1); } + + // touch each page to make sure it exists. + char *eee = sbrk(0); + for(char *pp = a; pp < eee; pp += 4096) + *pp = 1; + lastaddr = (char*) (BIG-1); *lastaddr = 99; @@ -2087,7 +2093,12 @@ sbrkfail(char *s) for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ if((pids[i] = fork()) == 0){ // allocate a lot of memory - sbrk(BIG - (uint64)sbrk(0)); + char *p0 = sbrk(BIG - (uint64)sbrk(0)); + if((uint64)p0 != 0xffffffffffffffffLL){ + char *p1 = sbrk(0); + for(char *p2 = p0; p2 < p1; p2 += 4096) + *p2 = 1; + } write(fds[1], "x", 1); // sit around until killed for(;;) sleep(1000); @@ -2469,6 +2480,7 @@ execout(char *s) uint64 a = (uint64) sbrk(4096); if(a == 0xffffffffffffffffLL) break; + *(char*)(a + 4096 - 1) = 1; } // free a few pages, in order to let exec() make some @@ -2503,7 +2515,7 @@ countfree() break; } // modify the memory to make sure it's really allocated. - *(char *)(a - 1) = 1; + *(char *)(a + 4096 - 1) = 1; n += 1; } sbrk(-((uint64)sbrk(0) - sz0)); -- cgit v1.2.3 From efaa7b8e2ae2ae059f3985bda718d91f3501b294 Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Fri, 21 Aug 2020 10:55:34 -0400 Subject: Adjust a few prototypes to be explicit that they don't take arguments --- kernel/defs.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/defs.h b/kernel/defs.h index 4eedd89..4b9bbc0 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -62,13 +62,13 @@ void ramdiskrw(struct buf*); // kalloc.c void* kalloc(void); void kfree(void *); -void kinit(); +void kinit(void); // log.c void initlog(int, struct superblock*); void log_write(struct buf*); -void begin_op(); -void end_op(); +void begin_op(void); +void end_op(void); // pipe.c int pipealloc(struct file**, struct file**); @@ -181,7 +181,7 @@ void plic_complete(int); // virtio_disk.c void virtio_disk_init(void); void virtio_disk_rw(struct buf *, int); -void virtio_disk_intr(); +void virtio_disk_intr(void); // number of elements in fixed-size array #define NELEM(x) (sizeof(x)/sizeof((x)[0])) -- cgit v1.2.3 From d7e5f269106f4b76fcbd90331a3b44f879ee9c54 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 27 Aug 2020 06:21:10 -0400 Subject: fix usertests to pass all the riscv-sol-fall20 solutions. --- user/usertests.c | 119 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 97 insertions(+), 22 deletions(-) diff --git a/user/usertests.c b/user/usertests.c index 502621a..db042e6 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -997,6 +997,11 @@ mem(char *s) } else { int xstatus; wait(&xstatus); + if(xstatus == -1){ + // probably page fault, so might be lazy lab, + // so OK. + exit(0); + } exit(xstatus); } } @@ -1955,9 +1960,31 @@ sbrkbasic(char *s) char *c, *a, *b; // does sbrk() return the expected failure value? - a = sbrk(TOOMUCH); - if(a != (char*)0xffffffffffffffffL){ - printf("%s: sbrk() returned %p\n", a); + pid = fork(); + if(pid < 0){ + printf("fork failed in sbrkbasic\n"); + exit(1); + } + if(pid == 0){ + a = sbrk(TOOMUCH); + if(a == (char*)0xffffffffffffffffL){ + // it's OK if this fails. + exit(0); + } + + for(b = a; b < a+TOOMUCH; b += 4096){ + *b = 99; + } + + // we should not get here! either sbrk(TOOMUCH) + // should have failed, or (with lazy allocation) + // a pagefault should have killed this process. + exit(1); + } + + wait(&xstatus); + if(xstatus == 1){ + printf("%s: too much memory allocated!\n", s); exit(1); } @@ -2093,12 +2120,7 @@ sbrkfail(char *s) for(i = 0; i < sizeof(pids)/sizeof(pids[0]); i++){ if((pids[i] = fork()) == 0){ // allocate a lot of memory - char *p0 = sbrk(BIG - (uint64)sbrk(0)); - if((uint64)p0 != 0xffffffffffffffffLL){ - char *p1 = sbrk(0); - for(char *p2 = p0; p2 < p1; p2 += 4096) - *p2 = 1; - } + sbrk(BIG - (uint64)sbrk(0)); write(fds[1], "x", 1); // sit around until killed for(;;) sleep(1000); @@ -2128,18 +2150,22 @@ sbrkfail(char *s) exit(1); } if(pid == 0){ - // allocate a lot of memory + // allocate a lot of memory. + // this should produce a page fault, + // and thus not complete. a = sbrk(0); sbrk(10*BIG); int n = 0; for (i = 0; i < 10*BIG; i += PGSIZE) { n += *(a+i); } + // print n so the compiler doesn't optimize away + // the for loop. printf("%s: allocate a lot of memory succeeded %d\n", n); exit(1); } wait(&xstatus); - if(xstatus != -1) + if(xstatus != -1 && xstatus != 2) exit(1); } @@ -2502,23 +2528,67 @@ execout(char *s) // // use sbrk() to count how many free physical memory pages there are. +// touches the pages to force allocation. +// because out of memory with lazy allocation results in the process +// taking a fault and being killed, fork and report back. // int countfree() { - uint64 sz0 = (uint64)sbrk(0); - int n = 0; + int fds[2]; + + if(pipe(fds) < 0){ + printf("pipe() failed in countfree()\n"); + exit(1); + } + + int pid = fork(); + + if(pid < 0){ + printf("fork failed in countfree()\n"); + exit(1); + } + + if(pid == 0){ + close(fds[0]); + + while(1){ + uint64 a = (uint64) sbrk(4096); + if(a == 0xffffffffffffffff){ + break; + } + + // modify the memory to make sure it's really allocated. + *(char *)(a + 4096 - 1) = 1; + + // report back one more page. + if(write(fds[1], "x", 1) != 1){ + printf("write() failed in countfree()\n"); + exit(1); + } + } + + exit(0); + } + close(fds[1]); + + int n = 0; while(1){ - uint64 a = (uint64) sbrk(4096); - if(a == 0xffffffffffffffff){ - break; + char c; + int cc = read(fds[0], &c, 1); + if(cc < 0){ + printf("read() failed in countfree()\n"); + exit(1); } - // modify the memory to make sure it's really allocated. - *(char *)(a + 4096 - 1) = 1; + if(cc == 0) + break; n += 1; } - sbrk(-((uint64)sbrk(0) - sz0)); + + close(fds[0]); + wait((int*)0); + return n; } @@ -2645,7 +2715,7 @@ main(int argc, char *argv[]) } int free1 = countfree(); if(free1 < free0){ - printf("FAILED -- lost some free pages\n"); + printf("FAILED -- lost %d free pages\n", free0 - free1); if(continuous != 2) exit(1); } @@ -2653,7 +2723,12 @@ main(int argc, char *argv[]) } printf("usertests starting\n"); +#ifdef SOL_LAZY1 + int free0 = 32*1024*1024; +#else int free0 = countfree(); + int free1 = 0; +#endif int fail = 0; for (struct test *t = tests; t->s != 0; t++) { if((justone == 0) || strcmp(t->s, justone) == 0) { @@ -2665,8 +2740,8 @@ main(int argc, char *argv[]) if(fail){ printf("SOME TESTS FAILED\n"); exit(1); - } else if(countfree() < free0){ - printf("FAILED -- lost some free pages\n"); + } else if((free1 = countfree()) < free0){ + printf("FAILED -- lost some free pages %d (out of %d)\n", free1, free0); exit(1); } else { printf("ALL TESTS PASSED\n"); -- cgit v1.2.3 From 2ec9c6ed66629fd5b833f06af2080eab46c0af35 Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Thu, 27 Aug 2020 07:05:13 -0400 Subject: Remove obselete ifdef that shouldn't have been there ever --- user/usertests.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/user/usertests.c b/user/usertests.c index db042e6..004c948 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -2723,12 +2723,8 @@ main(int argc, char *argv[]) } printf("usertests starting\n"); -#ifdef SOL_LAZY1 - int free0 = 32*1024*1024; -#else int free0 = countfree(); int free1 = 0; -#endif int fail = 0; for (struct test *t = tests; t->s != 0; t++) { if((justone == 0) || strcmp(t->s, justone) == 0) { -- cgit v1.2.3 From db067d24da0b6472afe9ce4165e0868772b11964 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 28 Aug 2020 05:44:09 -0400 Subject: suppress write() output after panic() --- kernel/console.c | 6 ++++++ kernel/printf.c | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/kernel/console.c b/kernel/console.c index 2b1ed3c..1885593 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -66,6 +66,12 @@ int consolewrite(int user_src, uint64 src, int n) { int i; + extern volatile int panicked; // from printf.c + + if(panicked){ + for(;;) + ; + } acquire(&cons.lock); for(i = 0; i < n; i++){ diff --git a/kernel/printf.c b/kernel/printf.c index 777cc5f..1a83284 100644 --- a/kernel/printf.c +++ b/kernel/printf.c @@ -121,7 +121,7 @@ panic(char *s) printf("panic: "); printf(s); printf("\n"); - panicked = 1; // freeze other CPUs + panicked = 1; // freeze output from other CPUs for(;;) ; } -- cgit v1.2.3 From ffb2ee074a42ed30a87fd6804682fa62eca13500 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 28 Aug 2020 05:51:48 -0400 Subject: move panicked check to uart.c --- kernel/console.c | 13 ------------- kernel/printf.c | 2 +- kernel/uart.c | 13 +++++++++++++ user/grind.c | 20 ++++++++++++++++++-- 4 files changed, 32 insertions(+), 16 deletions(-) diff --git a/kernel/console.c b/kernel/console.c index 1885593..d606ed2 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -33,13 +33,6 @@ void consputc(int c) { - extern volatile int panicked; // from printf.c - - if(panicked){ - for(;;) - ; - } - if(c == BACKSPACE){ // if the user typed backspace, overwrite with a space. uartputc_sync('\b'); uartputc_sync(' '); uartputc_sync('\b'); @@ -66,12 +59,6 @@ int consolewrite(int user_src, uint64 src, int n) { int i; - extern volatile int panicked; // from printf.c - - if(panicked){ - for(;;) - ; - } acquire(&cons.lock); for(i = 0; i < n; i++){ diff --git a/kernel/printf.c b/kernel/printf.c index 1a83284..e1347de 100644 --- a/kernel/printf.c +++ b/kernel/printf.c @@ -121,7 +121,7 @@ panic(char *s) printf("panic: "); printf(s); printf("\n"); - panicked = 1; // freeze output from other CPUs + panicked = 1; // freeze uart output from other CPUs for(;;) ; } diff --git a/kernel/uart.c b/kernel/uart.c index daf9f04..ce89615 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -45,6 +45,8 @@ 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++] +extern volatile int panicked; // from printf.c + void uartstart(); void @@ -85,6 +87,12 @@ void uartputc(int c) { acquire(&uart_tx_lock); + + if(panicked){ + for(;;) + ; + } + while(1){ if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){ // buffer is full. @@ -109,6 +117,11 @@ uartputc_sync(int c) { push_off(); + if(panicked){ + for(;;) + ; + } + // wait for Transmit Holding Empty to be set in LSR. while((ReadReg(LSR) & LSR_TX_IDLE) == 0) ; diff --git a/user/grind.c b/user/grind.c index 14e2aae..d11b20b 100644 --- a/user/grind.c +++ b/user/grind.c @@ -292,8 +292,8 @@ go(int which_child) } } -int -main() +void +iter() { unlink("a"); unlink("b"); @@ -331,3 +331,19 @@ main() exit(0); } + +int +main() +{ + while(1){ + int pid = fork(); + if(pid == 0){ + iter(); + exit(0); + } + if(pid > 0){ + wait(0); + } + sleep(20); + } +} -- cgit v1.2.3 From e23d53f27f6e187732a92a2b4705639d1c260f05 Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Fri, 28 Aug 2020 16:21:07 -0400 Subject: Delete doc dir because they take much space in student submissions --- doc/FU540-C000-v1.0.pdf | Bin 2277037 -> 0 bytes doc/riscv-calling.pdf | Bin 138193 -> 0 bytes doc/riscv-privileged-20190608-1.pdf | Bin 533858 -> 0 bytes doc/riscv-spec-20191213.pdf | Bin 1021610 -> 0 bytes doc/virtio-v1.1-csprd01.pdf | Bin 694936 -> 0 bytes 5 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 doc/FU540-C000-v1.0.pdf delete mode 100644 doc/riscv-calling.pdf delete mode 100644 doc/riscv-privileged-20190608-1.pdf delete mode 100644 doc/riscv-spec-20191213.pdf delete mode 100644 doc/virtio-v1.1-csprd01.pdf diff --git a/doc/FU540-C000-v1.0.pdf b/doc/FU540-C000-v1.0.pdf deleted file mode 100644 index 1a8cc69..0000000 Binary files a/doc/FU540-C000-v1.0.pdf and /dev/null differ diff --git a/doc/riscv-calling.pdf b/doc/riscv-calling.pdf deleted file mode 100644 index a3351b1..0000000 Binary files a/doc/riscv-calling.pdf and /dev/null differ diff --git a/doc/riscv-privileged-20190608-1.pdf b/doc/riscv-privileged-20190608-1.pdf deleted file mode 100644 index 2303a01..0000000 Binary files a/doc/riscv-privileged-20190608-1.pdf and /dev/null differ diff --git a/doc/riscv-spec-20191213.pdf b/doc/riscv-spec-20191213.pdf deleted file mode 100644 index 533c1cb..0000000 Binary files a/doc/riscv-spec-20191213.pdf and /dev/null differ diff --git a/doc/virtio-v1.1-csprd01.pdf b/doc/virtio-v1.1-csprd01.pdf deleted file mode 100644 index c7be62b..0000000 Binary files a/doc/virtio-v1.1-csprd01.pdf and /dev/null differ -- cgit v1.2.3 From 2055fe13ac413a53d74baafdfa47e4c892ff9dc9 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Sun, 6 Sep 2020 14:20:18 -0400 Subject: better grind error messages --- user/grind.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/user/grind.c b/user/grind.c index d11b20b..85899f5 100644 --- a/user/grind.c +++ b/user/grind.c @@ -57,7 +57,7 @@ go(int which_child) mkdir("grindir"); if(chdir("grindir") != 0){ - printf("chdir grindir failed\n"); + printf("grind: chdir grindir failed\n"); exit(1); } chdir("/"); @@ -75,7 +75,7 @@ go(int which_child) unlink("grindir/../a"); } else if(what == 4){ if(chdir("grindir") != 0){ - printf("chdir grindir failed\n"); + printf("grind: chdir grindir failed\n"); exit(1); } unlink("../b"); @@ -139,7 +139,7 @@ go(int which_child) exit(1); } if(chdir("../grindir/..") != 0){ - printf("chdir failed\n"); + printf("grind: chdir failed\n"); exit(1); } kill(pid); @@ -188,7 +188,7 @@ go(int which_child) unlink("x"); exit(0); } else if(pid < 0){ - printf("fork failed\n"); + printf("grind: fork failed\n"); exit(1); } wait(0); @@ -198,24 +198,24 @@ go(int which_child) // file descriptors, blocks. int fd1 = open("c", O_CREATE|O_RDWR); if(fd1 < 0){ - printf("create c failed\n"); + printf("grind: create c failed\n"); exit(1); } if(write(fd1, "x", 1) != 1){ - printf("write c failed\n"); + printf("grind: write c failed\n"); exit(1); } struct stat st; if(fstat(fd1, &st) != 0){ - printf("fstat failed\n"); + printf("grind: fstat failed\n"); exit(1); } if(st.size != 1){ - printf("fstat reports wrong size %d\n", (int)st.size); + printf("grind: 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); + printf("grind: fstat reports crazy i-number %d\n", st.ino); exit(1); } close(fd1); @@ -224,11 +224,11 @@ go(int which_child) // echo hi | cat int aa[2], bb[2]; if(pipe(aa) < 0){ - fprintf(2, "pipe failed\n"); + fprintf(2, "grind: pipe failed\n"); exit(1); } if(pipe(bb) < 0){ - fprintf(2, "pipe failed\n"); + fprintf(2, "grind: pipe failed\n"); exit(1); } int pid1 = fork(); @@ -238,16 +238,16 @@ go(int which_child) close(aa[0]); close(1); if(dup(aa[1]) != 1){ - fprintf(2, "dup failed\n"); + fprintf(2, "grind: dup failed\n"); exit(1); } close(aa[1]); char *args[3] = { "echo", "hi", 0 }; exec("grindir/../echo", args); - fprintf(2, "echo: not found\n"); + fprintf(2, "grind: echo: not found\n"); exit(2); } else if(pid1 < 0){ - fprintf(2, "fork failed\n"); + fprintf(2, "grind: fork failed\n"); exit(3); } int pid2 = fork(); @@ -256,22 +256,22 @@ go(int which_child) close(bb[0]); close(0); if(dup(aa[0]) != 0){ - fprintf(2, "dup failed\n"); + fprintf(2, "grind: dup failed\n"); exit(4); } close(aa[0]); close(1); if(dup(bb[1]) != 1){ - fprintf(2, "dup failed\n"); + fprintf(2, "grind: dup failed\n"); exit(5); } close(bb[1]); char *args[2] = { "cat", 0 }; exec("/cat", args); - fprintf(2, "cat: not found\n"); + fprintf(2, "grind: cat: not found\n"); exit(6); } else if(pid2 < 0){ - fprintf(2, "fork failed\n"); + fprintf(2, "grind: fork failed\n"); exit(7); } close(aa[0]); @@ -285,7 +285,7 @@ go(int which_child) wait(&st1); wait(&st2); if(st1 != 0 || st2 != 0 || strcmp(buf, "hi") != 0){ - printf("exec pipeline failed %d %d \"%s\"\n", st1, st2, buf); + printf("grind: exec pipeline failed %d %d \"%s\"\n", st1, st2, buf); exit(1); } } -- cgit v1.2.3 From 05074badc447ce9532824924bfc41a003579beb0 Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Thu, 24 Sep 2020 13:18:54 -0400 Subject: When either_copyin/out fails, return an error from write/read Add a test to check that read/write return an error --- kernel/fs.c | 2 ++ user/usertests.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/kernel/fs.c b/kernel/fs.c index ec68cd7..848b2c9 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -468,6 +468,7 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n) m = min(n - tot, BSIZE - off%BSIZE); if(either_copyout(user_dst, dst, bp->data + (off % BSIZE), m) == -1) { brelse(bp); + tot = -1; break; } brelse(bp); @@ -495,6 +496,7 @@ writei(struct inode *ip, int user_src, uint64 src, uint off, uint n) m = min(n - tot, BSIZE - off%BSIZE); if(either_copyin(bp->data + (off % BSIZE), user_src, src, m) == -1) { brelse(bp); + n = -1; break; } log_write(bp); diff --git a/user/usertests.c b/user/usertests.c index 004c948..7300574 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -233,6 +233,53 @@ copyinstr3(char *s) } } +// See if the kernel refuses to read/write user memory that the +// application doesn't have anymore, because it returned it. +void +rwsbrk() +{ + int fd, n; + + uint64 a = (uint64) sbrk(8192); + + if(a == 0xffffffffffffffffLL) { + printf("sbrk(rwsbrk) failed\n"); + exit(1); + } + + if ((uint64) sbrk(-8192) == 0xffffffffffffffffLL) { + printf("sbrk(rwsbrk) shrink failed\n"); + exit(1); + } + + fd = open("rwsbrk", O_CREATE|O_WRONLY); + if(fd < 0){ + printf("open(rwsbrk) failed\n"); + exit(1); + } + n = write(fd, (void*)(a+4096), 1024); + if(n >= 0){ + printf("write(fd, %p, 1024) returned %d, not -1\n", a+4096, n); + exit(1); + } + close(fd); + unlink("rwsbrk"); + + fd = open("README", O_RDONLY); + if(fd < 0){ + printf("open(rwsbrk) failed\n"); + exit(1); + } + n = read(fd, (void*)(a+4096), 10); + if(n >= 0){ + printf("read(fd, %p, 10) returned %d, not -1\n", a+4096, n); + exit(1); + } + close(fd); + + exit(0); +} + // test O_TRUNC. void truncate1(char *s) @@ -2644,6 +2691,7 @@ main(int argc, char *argv[]) {copyinstr1, "copyinstr1"}, {copyinstr2, "copyinstr2"}, {copyinstr3, "copyinstr3"}, + {rwsbrk, "rwsbrk" }, {truncate1, "truncate1"}, {truncate2, "truncate2"}, {truncate3, "truncate3"}, -- cgit v1.2.3 From d9c7b13acce9ac17e550ec02799edf8da059d78a Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Fri, 2 Oct 2020 07:51:15 -0400 Subject: Add s to many printf statements that expect it (thanks Cece Chu) Add dirtest to the list of tests --- user/usertests.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/user/usertests.c b/user/usertests.c index 7300574..b7c318a 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -543,11 +543,11 @@ writetest(char *s) } for(i = 0; i < N; i++){ if(write(fd, "aaaaaaaaaa", SZ) != SZ){ - printf("%s: error: write aa %d new file failed\n", i); + printf("%s: error: write aa %d new file failed\n", s, i); exit(1); } if(write(fd, "bbbbbbbbbb", SZ) != SZ){ - printf("%s: error: write bb %d new file failed\n", i); + printf("%s: error: write bb %d new file failed\n", s, i); exit(1); } } @@ -584,7 +584,7 @@ writebig(char *s) for(i = 0; i < MAXFILE; i++){ ((int*)buf)[0] = i; if(write(fd, buf, BSIZE) != BSIZE){ - printf("%s: error: write big file failed\n", i); + printf("%s: error: write big file failed\n", s, i); exit(1); } } @@ -602,16 +602,16 @@ writebig(char *s) i = read(fd, buf, BSIZE); if(i == 0){ if(n == MAXFILE - 1){ - printf("%s: read only %d blocks from big", n); + printf("%s: read only %d blocks from big", s, n); exit(1); } break; } else if(i != BSIZE){ - printf("%s: read failed %d\n", i); + printf("%s: read failed %d\n", s, i); exit(1); } if(((int*)buf)[0] != n){ - printf("%s: read content of block %d is %d\n", + printf("%s: read content of block %d is %d\n", s, n, ((int*)buf)[0]); exit(1); } @@ -648,8 +648,6 @@ createtest(char *s) void dirtest(char *s) { - printf("mkdir test\n"); - if(mkdir("dir0") < 0){ printf("%s: mkdir failed\n", s); exit(1); @@ -669,7 +667,6 @@ void dirtest(char *s) printf("%s: unlink dir0 failed\n", s); exit(1); } - printf("%s: mkdir test ok\n"); } void @@ -791,7 +788,7 @@ preempt(char *s) pid1 = fork(); if(pid1 < 0) { - printf("%s: fork failed"); + printf("%s: fork failed", s); exit(1); } if(pid1 == 0) @@ -816,7 +813,7 @@ preempt(char *s) if(pid3 == 0){ close(pfds[0]); if(write(pfds[1], "x", 1) != 1) - printf("%s: preempt write error"); + printf("%s: preempt write error", s); close(pfds[1]); for(;;) ; @@ -824,7 +821,7 @@ preempt(char *s) close(pfds[1]); if(read(pfds[0], buf, sizeof(buf)) != 1){ - printf("%s: preempt read error"); + printf("%s: preempt read error", s); return; } close(pfds[0]); @@ -2098,7 +2095,7 @@ sbrkmuch(char *s) } c = sbrk(0); if(c != a - PGSIZE){ - printf("%s: sbrk deallocation produced wrong address, a %x c %x\n", a, c); + printf("%s: sbrk deallocation produced wrong address, a %x c %x\n", s, a, c); exit(1); } @@ -2106,7 +2103,7 @@ sbrkmuch(char *s) a = sbrk(0); c = sbrk(PGSIZE); if(c != a || sbrk(0) != a + PGSIZE){ - printf("%s: sbrk re-allocation failed, a %x c %x\n", a, c); + printf("%s: sbrk re-allocation failed, a %x c %x\n", s, a, c); exit(1); } if(*lastaddr == 99){ @@ -2118,7 +2115,7 @@ sbrkmuch(char *s) a = sbrk(0); c = sbrk(-(sbrk(0) - oldbrk)); if(c != a){ - printf("%s: sbrk downsize failed, a %x c %x\n", a, c); + printf("%s: sbrk downsize failed, a %x c %x\n", s, a, c); exit(1); } } @@ -2137,7 +2134,7 @@ kernmem(char *s) exit(1); } if(pid == 0){ - printf("%s: oops could read %x = %x\n", a, *a); + printf("%s: oops could read %x = %x\n", s, a, *a); exit(1); } int xstatus; @@ -2208,7 +2205,7 @@ sbrkfail(char *s) } // print n so the compiler doesn't optimize away // the for loop. - printf("%s: allocate a lot of memory succeeded %d\n", n); + printf("%s: allocate a lot of memory succeeded %d\n", s, n); exit(1); } wait(&xstatus); @@ -2330,10 +2327,10 @@ fsfull() name[3] = '0' + (nfiles % 100) / 10; name[4] = '0' + (nfiles % 10); name[5] = '\0'; - printf("%s: writing %s\n", name); + printf("writing %s\n", name); int fd = open(name, O_CREATE|O_RDWR); if(fd < 0){ - printf("%s: open %s failed\n", name); + printf("open %s failed\n", name); break; } int total = 0; @@ -2344,7 +2341,7 @@ fsfull() total += cc; fsblocks++; } - printf("%s: wrote %d bytes\n", total); + printf("wrote %d bytes\n", total); close(fd); if(total == 0) break; @@ -2398,7 +2395,7 @@ stacktest(char *s) char *sp = (char *) r_sp(); sp -= PGSIZE; // the *sp should cause a trap. - printf("%s: stacktest: read below stack %p\n", *sp); + printf("%s: stacktest: read below stack %p\n", s, *sp); exit(1); } else if(pid < 0){ printf("%s: fork failed\n", s); @@ -2713,6 +2710,7 @@ main(int argc, char *argv[]) {subdir, "subdir"}, {fourfiles, "fourfiles"}, {sharedfd, "sharedfd"}, + {dirtest, "dirtest"}, {exectest, "exectest"}, {bigargtest, "bigargtest"}, {bigwrite, "bigwrite"}, -- cgit v1.2.3 From 1c4b582fc74a3b6192f7a1aba1c667b1b5e840bd Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Sat, 3 Oct 2020 10:21:04 -0400 Subject: Clarify CLINT and PLIC acronym --- kernel/memlayout.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/memlayout.h b/kernel/memlayout.h index b6e595d..776f98c 100644 --- a/kernel/memlayout.h +++ b/kernel/memlayout.h @@ -25,12 +25,12 @@ #define VIRTIO0 0x10001000 #define VIRTIO0_IRQ 1 -// local interrupt controller, which contains the timer. +// core local interruptor (CLINT), which contains the timer. #define CLINT 0x2000000L #define CLINT_MTIMECMP(hartid) (CLINT + 0x4000 + 8*(hartid)) #define CLINT_MTIME (CLINT + 0xBFF8) // cycles since boot. -// qemu puts programmable interrupt controller here. +// qemu puts platform-level interrupt controller (PLIC) here. #define PLIC 0x0c000000L #define PLIC_PRIORITY (PLIC + 0x0) #define PLIC_PENDING (PLIC + 0x1000) -- cgit v1.2.3 From b96547403df0e07865bc2269215cfeb0260b893d Mon Sep 17 00:00:00 2001 From: Fumiya Shigemitsu Date: Mon, 21 Oct 2019 21:01:07 +0900 Subject: Fix minor typos --- kernel/vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/vm.c b/kernel/vm.c index bccb405..0809aea 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -68,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. -pte_t * +static pte_t * walk(pagetable_t pagetable, uint64 va, int alloc) { if(va >= MAXVA) -- cgit v1.2.3 From b9359c353318ba70596b55ec57f8d9f6c43c2758 Mon Sep 17 00:00:00 2001 From: Matt Harvey Date: Mon, 14 Sep 2020 14:49:57 -0700 Subject: Corrects order of UART RX/TX interrupt enable bits (per http://byterunner.com/16550.html and manually tested in qemu bare metal echo) --- kernel/uart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/uart.c b/kernel/uart.c index ce89615..d586ea4 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -22,8 +22,8 @@ #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 IER_RX_ENABLE (1<<0) +#define IER_TX_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 -- cgit v1.2.3 From e3672e018aaaadafccc3634911d69f46ec4b4819 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Sun, 4 Oct 2020 08:44:32 -0400 Subject: often causes deadlock after a few minutes --- user/usertests.c | 60 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/user/usertests.c b/user/usertests.c index b7c318a..720e3cf 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -17,10 +17,9 @@ // prints "OK". // -#define BUFSZ (MAXOPBLOCKS+2)*BSIZE +#define BUFSZ ((MAXOPBLOCKS+2)*BSIZE) char buf[BUFSZ]; -char name[3]; // what if you pass ridiculous pointers to system calls // that read user memory with copyin? @@ -631,6 +630,7 @@ createtest(char *s) int i, fd; enum { N=52 }; + char name[3]; name[0] = 'a'; name[2] = '\0'; for(i = 0; i < N; i++){ @@ -1728,6 +1728,61 @@ bigwrite(char *s) } } +// concurrent writes to try to provoke deadlock in the virtio disk +// driver. +void +manywrites(char *s) +{ + int nchildren = 4; + + for(int ci = 0; ci < nchildren; ci++){ + int pid = fork(); + if(pid < 0){ + printf("fork failed\n"); + exit(1); + } + + if(pid == 0){ + char name[3]; + name[0] = 'b'; + name[1] = 'a' + ci; + name[2] = '\0'; + unlink(name); + + for(int iters = 0; iters < 500000; iters++){ + for(int i = 0; i < ci+1; i++){ + int fd = open(name, O_CREATE | O_RDWR); + if(fd < 0){ + printf("%s: cannot create %s\n", s, name); + exit(1); + } + int sz = sizeof(buf); + int cc = write(fd, buf, sz); + if(cc != sz){ + printf("%s: write(%d) ret %d\n", s, sz, cc); + exit(1); + } + close(fd); + } + unlink(name); + if((iters % 50) == ci) + write(1, ".", 1); + } + + unlink(name); + exit(0); + } + } + + for(int ci = 0; ci < nchildren; ci++){ + int st = 0; + wait(&st); + if(st != 0) + exit(st); + } + exit(0); +} + void bigfile(char *s) { @@ -2682,6 +2737,7 @@ main(int argc, char *argv[]) void (*f)(char *); char *s; } tests[] = { + // {manywrites, "manywrites"}, {execout, "execout"}, {copyin, "copyin"}, {copyout, "copyout"}, -- cgit v1.2.3 From 792d60e91224bc15ce41aaff2560a82b63fdf06f Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Sun, 4 Oct 2020 09:21:03 -0400 Subject: avoid deadlock by disk intr acking interrupt first, then processing ring --- kernel/virtio_disk.c | 51 ++++++++++++++++++++++++++++++++++----------------- user/usertests.c | 7 +++---- 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/kernel/virtio_disk.c b/kernel/virtio_disk.c index 06e0645..1dbd6ed 100644 --- a/kernel/virtio_disk.c +++ b/kernel/virtio_disk.c @@ -130,10 +130,13 @@ static void free_desc(int i) { if(i >= NUM) - panic("virtio_disk_intr 1"); + panic("free_desc 1"); if(disk.free[i]) - panic("virtio_disk_intr 2"); + panic("free_desc 2"); disk.desc[i].addr = 0; + disk.desc[i].len = 0; + disk.desc[i].flags = 0; + disk.desc[i].next = 0; disk.free[i] = 1; wakeup(&disk.free[0]); } @@ -143,9 +146,11 @@ static void free_chain(int i) { while(1){ + int flag = disk.desc[i].flags; + int nxt = disk.desc[i].next; free_desc(i); - if(disk.desc[i].flags & VRING_DESC_F_NEXT) - i = disk.desc[i].next; + if(flag & VRING_DESC_F_NEXT) + i = nxt; else break; } @@ -184,7 +189,7 @@ virtio_disk_rw(struct buf *b, int write) } sleep(&disk.free[0], &disk.vdisk_lock); } - + // format the three descriptors. // qemu's virtio-blk.c reads them. @@ -217,7 +222,7 @@ virtio_disk_rw(struct buf *b, int write) disk.desc[idx[1]].flags |= VRING_DESC_F_NEXT; disk.desc[idx[1]].next = idx[2]; - disk.info[idx[0]].status = 0; + disk.info[idx[0]].status = 0xff; // device writes 0 on success disk.desc[idx[2]].addr = (uint64) &disk.info[idx[0]].status; disk.desc[idx[2]].len = 1; disk.desc[idx[2]].flags = VRING_DESC_F_WRITE; // device writes the status @@ -227,13 +232,17 @@ virtio_disk_rw(struct buf *b, int write) b->disk = 1; disk.info[idx[0]].b = b; - // avail[0] is flags - // avail[1] tells the device how far to look in avail[2...]. - // avail[2...] are desc[] indices the device should process. + // avail[0] is flags (always zero) + // avail[1] is an index into avail[2...] telling where we'll write next + // avail[2...] is a ring of NUM indices the device should process // we only tell device the first index in our chain of descriptors. disk.avail[2 + (disk.avail[1] % NUM)] = idx[0]; + + __sync_synchronize(); + + disk.avail[1] = disk.avail[1] + 1; // not % NUM ... + __sync_synchronize(); - disk.avail[1] = disk.avail[1] + 1; *R(VIRTIO_MMIO_QUEUE_NOTIFY) = 0; // value is queue number @@ -253,18 +262,26 @@ virtio_disk_intr() { acquire(&disk.vdisk_lock); - while((disk.used_idx % NUM) != (disk.used->id % NUM)){ - int id = disk.used->elems[disk.used_idx].id; + // this ack may race with the device writing new notifications to + // the "used" ring, in which case we may get an interrupt we don't + // need, which is harmless. + *R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3; + + __sync_synchronize(); + + while(disk.used_idx != disk.used->id){ + __sync_synchronize(); + int id = disk.used->elems[disk.used_idx % NUM].id; if(disk.info[id].status != 0) panic("virtio_disk_intr status"); - - disk.info[id].b->disk = 0; // disk is done with buf - wakeup(disk.info[id].b); - disk.used_idx = (disk.used_idx + 1) % NUM; + struct buf *b = disk.info[id].b; + b->disk = 0; // disk is done with buf + wakeup(b); + + disk.used_idx += 1; } - *R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3; release(&disk.vdisk_lock); } diff --git a/user/usertests.c b/user/usertests.c index 720e3cf..ec6630d 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -1734,6 +1734,7 @@ void manywrites(char *s) { int nchildren = 4; + int howmany = 30; // increase to look for deadlock for(int ci = 0; ci < nchildren; ci++){ int pid = fork(); @@ -1749,7 +1750,7 @@ manywrites(char *s) name[2] = '\0'; unlink(name); - for(int iters = 0; iters < 500000; iters++){ + for(int iters = 0; iters < howmany; iters++){ for(int i = 0; i < ci+1; i++){ int fd = open(name, O_CREATE | O_RDWR); if(fd < 0){ @@ -1765,8 +1766,6 @@ manywrites(char *s) close(fd); } unlink(name); - if((iters % 50) == ci) - write(1, ".", 1); } unlink(name); @@ -2737,7 +2736,7 @@ main(int argc, char *argv[]) void (*f)(char *); char *s; } tests[] = { - // {manywrites, "manywrites"}, + {manywrites, "manywrites"}, {execout, "execout"}, {copyin, "copyin"}, {copyout, "copyout"}, -- cgit v1.2.3 From da002a48fbbf69a0d965755d9f6b66817e8a15a5 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Sun, 4 Oct 2020 13:29:04 -0400 Subject: don't unpin if recovering -- the resulting negative refcnt suppresses next unpin --- kernel/log.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/kernel/log.c b/kernel/log.c index 5e884bb..c8af5bd 100644 --- a/kernel/log.c +++ b/kernel/log.c @@ -66,7 +66,7 @@ initlog(int dev, struct superblock *sb) // Copy committed blocks from log to their home location static void -install_trans(void) +install_trans(int recovering) { int tail; @@ -75,7 +75,8 @@ install_trans(void) struct buf *dbuf = bread(log.dev, log.lh.block[tail]); // read dst memmove(dbuf->data, lbuf->data, BSIZE); // copy block to dst bwrite(dbuf); // write dst to disk - bunpin(dbuf); + if(recovering == 0) + bunpin(dbuf); brelse(lbuf); brelse(dbuf); } @@ -116,7 +117,7 @@ static void recover_from_log(void) { read_head(); - install_trans(); // if committed, copy from log to disk + install_trans(1); // if committed, copy from log to disk log.lh.n = 0; write_head(); // clear the log } @@ -195,7 +196,7 @@ commit() if (log.lh.n > 0) { write_log(); // Write modified blocks from cache to log write_head(); // Write header to disk -- the real commit - install_trans(); // Now install writes to home locations + install_trans(0); // Now install writes to home locations log.lh.n = 0; write_head(); // Erase the transaction from the log } -- cgit v1.2.3 From 271d89ae3065d397e26faa8e05aeec5cc3cd8934 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Mon, 5 Oct 2020 06:26:58 -0400 Subject: improve virtio_disk comments; bring it closer to wording in the spec --- kernel/virtio.h | 36 +++++++++++++++++----- kernel/virtio_disk.c | 86 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 85 insertions(+), 37 deletions(-) diff --git a/kernel/virtio.h b/kernel/virtio.h index 03b53a9..f1dc520 100644 --- a/kernel/virtio.h +++ b/kernel/virtio.h @@ -47,7 +47,8 @@ // must be a power of two. #define NUM 8 -struct VRingDesc { +// a single descriptor, from the spec. +struct virtq_desc { uint64 addr; uint32 len; uint16 flags; @@ -56,17 +57,38 @@ struct VRingDesc { #define VRING_DESC_F_NEXT 1 // chained with another descriptor #define VRING_DESC_F_WRITE 2 // device writes (vs read) -struct VRingUsedElem { +// the (entire) avail ring, from the spec. +struct virtq_avail { + uint16 flags; // always zero + uint16 idx; // driver will write ring[idx] next + uint16 ring[NUM]; // descriptor numbers of chain heads + uint16 unused; +}; + +// one entry in the "used" ring, with which the +// device tells the driver about completed requests. +struct virtq_used_elem { uint32 id; // index of start of completed descriptor chain uint32 len; }; -// for disk ops +struct virtq_used { + uint16 flags; // always zero + uint16 idx; // device increments when it adds a ring[] entry + struct virtq_used_elem ring[NUM]; +}; + +// these are specific to virtio block devices, e.g. disks, +// described in Section 5.2 of the spec. + #define VIRTIO_BLK_T_IN 0 // read the disk #define VIRTIO_BLK_T_OUT 1 // write the disk -struct UsedArea { - uint16 flags; - uint16 id; - struct VRingUsedElem elems[NUM]; +// the format of the first descriptor in a disk request. +// to be followed by two more descriptors containing +// the block, and a one-byte status. +struct virtio_blk_req { + uint32 type; // VIRTIO_BLK_T_IN or ..._OUT + uint32 reserved; + uint64 sector; }; diff --git a/kernel/virtio_disk.c b/kernel/virtio_disk.c index 1dbd6ed..070490a 100644 --- a/kernel/virtio_disk.c +++ b/kernel/virtio_disk.c @@ -21,14 +21,37 @@ #define R(r) ((volatile uint32 *)(VIRTIO0 + (r))) static struct disk { - // memory for virtio descriptors &c for queue 0. - // this is a global instead of allocated because it must - // be multiple contiguous pages, which kalloc() - // doesn't support, and page aligned. + // the virtio driver and device mostly communicate through a set of + // structures in RAM. pages[] allocates that memory. pages[] is a + // global (instead of calls to kalloc()) because it must consist of + // two contiguous pages of page-aligned physical memory. char pages[2*PGSIZE]; - struct VRingDesc *desc; - uint16 *avail; - struct UsedArea *used; + + // pages[] is divided into three regions (descriptors, avail, and + // used), as explained in Section 2.6 of the virtio specification + // for the legacy interface. + // https://docs.oasis-open.org/virtio/virtio/v1.1/virtio-v1.1.pdf + + // the first region of pages[] is a set (not a ring) of DMA + // descriptors, with which the driver tells the device where to read + // and write individual disk operations. there are NUM descriptors. + // most commands consist of a "chain" (a linked list) of a couple of + // these descriptors. + // points into pages[]. + struct virtq_desc *desc; + + // next is a ring in which the driver writes descriptor numbers + // that the driver would like the device to process. it only + // includes the head descriptor of each chain. the ring has + // NUM elements. + // points into pages[]. + struct virtq_avail *avail; + + // finally a ring in which the device writes descriptor numbers that + // the device has finished processing (just the head of each chain). + // there are NUM used ring entries. + // points into pages[]. + struct virtq_used *used; // our own book-keeping. char free[NUM]; // is a descriptor free? @@ -98,14 +121,15 @@ virtio_disk_init(void) memset(disk.pages, 0, sizeof(disk.pages)); *R(VIRTIO_MMIO_QUEUE_PFN) = ((uint64)disk.pages) >> PGSHIFT; - // desc = pages -- num * VRingDesc + // desc = pages -- num * virtq_desc // avail = pages + 0x40 -- 2 * uint16, then num * uint16 // used = pages + 4096 -- 2 * uint16, then num * vRingUsedElem - disk.desc = (struct VRingDesc *) disk.pages; - disk.avail = (uint16*)(((char*)disk.desc) + NUM*sizeof(struct VRingDesc)); - disk.used = (struct UsedArea *) (disk.pages + PGSIZE); + disk.desc = (struct virtq_desc *) disk.pages; + disk.avail = (struct virtq_avail *)(disk.pages + NUM*sizeof(struct virtq_desc)); + disk.used = (struct virtq_used *) (disk.pages + PGSIZE); + // all NUM descriptors start out unused. for(int i = 0; i < NUM; i++) disk.free[i] = 1; @@ -156,6 +180,8 @@ free_chain(int i) } } +// allocate three descriptors (they need not be contiguous). +// disk transfers always use three descriptors. static int alloc3_desc(int *idx) { @@ -177,9 +203,9 @@ virtio_disk_rw(struct buf *b, int write) acquire(&disk.vdisk_lock); - // the spec says that legacy block operations use three - // descriptors: one for type/reserved/sector, one for - // the data, one for a 1-byte status result. + // the spec's Section 5.2 says that legacy block operations use + // three descriptors: one for type/reserved/sector, one for the + // data, one for a 1-byte status result. // allocate the three descriptors. int idx[3]; @@ -193,11 +219,7 @@ virtio_disk_rw(struct buf *b, int write) // format the three descriptors. // qemu's virtio-blk.c reads them. - struct virtio_blk_outhdr { - uint32 type; - uint32 reserved; - uint64 sector; - } buf0; + struct virtio_blk_req buf0; if(write) buf0.type = VIRTIO_BLK_T_OUT; // write the disk @@ -232,15 +254,13 @@ virtio_disk_rw(struct buf *b, int write) b->disk = 1; disk.info[idx[0]].b = b; - // avail[0] is flags (always zero) - // avail[1] is an index into avail[2...] telling where we'll write next - // avail[2...] is a ring of NUM indices the device should process - // we only tell device the first index in our chain of descriptors. - disk.avail[2 + (disk.avail[1] % NUM)] = idx[0]; + // tell the device the first index in our chain of descriptors. + disk.avail->ring[disk.avail->idx % NUM] = idx[0]; __sync_synchronize(); - disk.avail[1] = disk.avail[1] + 1; // not % NUM ... + // tell the device another avail ring entry is available. + disk.avail->idx += 1; // not % NUM ... __sync_synchronize(); @@ -262,16 +282,22 @@ virtio_disk_intr() { acquire(&disk.vdisk_lock); - // this ack may race with the device writing new notifications to - // the "used" ring, in which case we may get an interrupt we don't - // need, which is harmless. + // the device won't raise another interrupt until we tell it + // we've seen this interrupt, which the following line does. + // this may race with the device writing new entries to + // the "used" ring, in which case we may process the new + // completion entries in this interrupt, and have nothing to do + // in the next interrupt, which is harmless. *R(VIRTIO_MMIO_INTERRUPT_ACK) = *R(VIRTIO_MMIO_INTERRUPT_STATUS) & 0x3; __sync_synchronize(); - while(disk.used_idx != disk.used->id){ + // the device increments disk.used->idx when it + // adds an entry to the used ring. + + while(disk.used_idx != disk.used->idx){ __sync_synchronize(); - int id = disk.used->elems[disk.used_idx % NUM].id; + int id = disk.used->ring[disk.used_idx % NUM].id; if(disk.info[id].status != 0) panic("virtio_disk_intr status"); -- cgit v1.2.3 From a9b3dd457c647d2836080af4f1cd7c7cb4d1a40d Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Mon, 5 Oct 2020 06:59:33 -0400 Subject: eliminate virtio DMA into kernel stacks. --- kernel/defs.h | 1 - kernel/virtio_disk.c | 22 ++++++++++++---------- kernel/vm.c | 20 -------------------- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/kernel/defs.h b/kernel/defs.h index 4b9bbc0..49aafdd 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -156,7 +156,6 @@ int uartgetc(void); // vm.c void kvminit(void); void kvminithart(void); -uint64 kvmpa(uint64); void kvmmap(uint64, uint64, uint64, int); int mappages(pagetable_t, uint64, uint64, uint64, int); pagetable_t uvmcreate(void); diff --git a/kernel/virtio_disk.c b/kernel/virtio_disk.c index 070490a..cca44cb 100644 --- a/kernel/virtio_disk.c +++ b/kernel/virtio_disk.c @@ -64,6 +64,10 @@ static struct disk { struct buf *b; char status; } info[NUM]; + + // disk command headers. + // one-for-one with descriptors, for convenience. + struct virtio_blk_req ops[NUM]; struct spinlock vdisk_lock; @@ -219,19 +223,17 @@ virtio_disk_rw(struct buf *b, int write) // format the three descriptors. // qemu's virtio-blk.c reads them. - struct virtio_blk_req buf0; + struct virtio_blk_req *buf0 = &disk.ops[idx[0]]; if(write) - buf0.type = VIRTIO_BLK_T_OUT; // write the disk + buf0->type = VIRTIO_BLK_T_OUT; // write the disk else - buf0.type = VIRTIO_BLK_T_IN; // read the disk - buf0.reserved = 0; - buf0.sector = sector; - - // buf0 is on a kernel stack, which is not direct mapped, - // thus the call to kvmpa(). - disk.desc[idx[0]].addr = (uint64) kvmpa((uint64) &buf0); - disk.desc[idx[0]].len = sizeof(buf0); + buf0->type = VIRTIO_BLK_T_IN; // read the disk + buf0->reserved = 0; + buf0->sector = sector; + + disk.desc[idx[0]].addr = (uint64) buf0; + disk.desc[idx[0]].len = sizeof(struct virtio_blk_req); disk.desc[idx[0]].flags = VRING_DESC_F_NEXT; disk.desc[idx[0]].next = idx[1]; diff --git a/kernel/vm.c b/kernel/vm.c index 0809aea..c99d2a5 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -121,26 +121,6 @@ kvmmap(uint64 va, uint64 pa, uint64 sz, int perm) panic("kvmmap"); } -// translate a kernel virtual address to -// a physical address. only needed for -// addresses on the stack. -// assumes va is page aligned. -uint64 -kvmpa(uint64 va) -{ - uint64 off = va % PGSIZE; - pte_t *pte; - uint64 pa; - - pte = walk(kernel_pagetable, va, 0); - if(pte == 0) - panic("kvmpa"); - if((*pte & PTE_V) == 0) - panic("kvmpa"); - pa = PTE2PA(*pte); - return pa+off; -} - // Create PTEs for virtual addresses starting at va that refer to // physical addresses starting at pa. va and size might not // be page-aligned. Returns 0 on success, -1 if walk() couldn't -- cgit v1.2.3 From 0c55849d28a2df0416d28c83bc9da47411e2c78f Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Mon, 5 Oct 2020 09:17:47 -0400 Subject: Don't map the CLINT, since it never used in the kernel in supervisor mode --- kernel/vm.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/kernel/vm.c b/kernel/vm.c index c99d2a5..252d97c 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -30,9 +30,6 @@ kvminit() // virtio mmio disk interface kvmmap(VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W); - // CLINT - kvmmap(CLINT, CLINT, 0x10000, PTE_R | PTE_W); - // PLIC kvmmap(PLIC, PLIC, 0x400000, PTE_R | PTE_W); -- cgit v1.2.3 From 6c167595037a0872338d999c7ce0971a5606dedb Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Mon, 5 Oct 2020 15:28:01 -0400 Subject: more explicable scratch area size for machine-mode timer interrupts --- kernel/kernelvec.S | 8 ++++---- kernel/start.c | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/kernel/kernelvec.S b/kernel/kernelvec.S index 3e9d3e9..f42a364 100644 --- a/kernel/kernelvec.S +++ b/kernel/kernelvec.S @@ -93,8 +93,8 @@ kernelvec: timervec: # start.c has set up the memory that mscratch points to: # scratch[0,8,16] : register save area. - # scratch[32] : address of CLINT's MTIMECMP register. - # scratch[40] : desired interval between interrupts. + # scratch[24] : address of CLINT's MTIMECMP register. + # scratch[32] : desired interval between interrupts. csrrw a0, mscratch, a0 sd a1, 0(a0) @@ -103,8 +103,8 @@ timervec: # schedule the next timer interrupt # by adding interval to mtimecmp. - ld a1, 32(a0) # CLINT_MTIMECMP(hart) - ld a2, 40(a0) # interval + ld a1, 24(a0) # CLINT_MTIMECMP(hart) + ld a2, 32(a0) # interval ld a3, 0(a1) add a3, a3, a2 sd a3, 0(a1) diff --git a/kernel/start.c b/kernel/start.c index 4eb6c2d..1876680 100644 --- a/kernel/start.c +++ b/kernel/start.c @@ -10,8 +10,8 @@ void timerinit(); // entry.S needs one stack per CPU. __attribute__ ((aligned (16))) char stack0[4096 * NCPU]; -// scratch area for timer interrupt, one per CPU. -uint64 mscratch0[NCPU * 32]; +// a scratch area per CPU for machine-mode timer interrupts. +uint64 timer_scratch[NCPU][5]; // assembly code in kernelvec.S for machine-mode timer interrupt. extern void timervec(); @@ -64,12 +64,12 @@ timerinit() *(uint64*)CLINT_MTIMECMP(id) = *(uint64*)CLINT_MTIME + interval; // prepare information in scratch[] for timervec. - // scratch[0..3] : space for timervec to save registers. - // scratch[4] : address of CLINT MTIMECMP register. - // scratch[5] : desired interval (in cycles) between timer interrupts. - uint64 *scratch = &mscratch0[32 * id]; - scratch[4] = CLINT_MTIMECMP(id); - scratch[5] = interval; + // scratch[0..2] : space for timervec to save registers. + // scratch[3] : address of CLINT MTIMECMP register. + // scratch[4] : desired interval (in cycles) between timer interrupts. + uint64 *scratch = &timer_scratch[id][0]; + scratch[3] = CLINT_MTIMECMP(id); + scratch[4] = interval; w_mscratch((uint64)scratch); // set the machine-mode trap handler. -- cgit v1.2.3 From 806580d6423274e7b38329362f64a549e04ddbab Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Wed, 7 Oct 2020 12:57:55 -0400 Subject: set riscv use-compressed-breakpoints yes --- .gdbinit.tmpl-riscv | 1 + 1 file changed, 1 insertion(+) diff --git a/.gdbinit.tmpl-riscv b/.gdbinit.tmpl-riscv index 6a38a95..a2bfde3 100644 --- a/.gdbinit.tmpl-riscv +++ b/.gdbinit.tmpl-riscv @@ -3,3 +3,4 @@ set architecture riscv:rv64 target remote 127.0.0.1:1234 symbol-file kernel/kernel set disassemble-next-line auto +set riscv use-compressed-breakpoints yes -- cgit v1.2.3 From c64aa44d7b5167f5b061b1e2fdf94d240a98b2bb Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Wed, 14 Oct 2020 20:03:14 -0400 Subject: kvmmake() makes a complete kernel page table, matching Figure 3.3 --- kernel/defs.h | 3 ++- kernel/proc.c | 29 ++++++++++++++++++----------- kernel/vm.c | 42 +++++++++++++++++++++++++++--------------- 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/kernel/defs.h b/kernel/defs.h index 49aafdd..41098f4 100644 --- a/kernel/defs.h +++ b/kernel/defs.h @@ -86,6 +86,7 @@ int cpuid(void); void exit(int); int fork(void); int growproc(int); +void proc_mapstacks(pagetable_t); pagetable_t proc_pagetable(struct proc *); void proc_freepagetable(pagetable_t, uint64); int kill(int); @@ -156,7 +157,7 @@ int uartgetc(void); // vm.c void kvminit(void); void kvminithart(void); -void kvmmap(uint64, uint64, uint64, int); +void kvmmap(pagetable_t, uint64, uint64, uint64, int); int mappages(pagetable_t, uint64, uint64, uint64, int); pagetable_t uvmcreate(void); void uvminit(pagetable_t, uchar *, uint); diff --git a/kernel/proc.c b/kernel/proc.c index 56314e5..d847c43 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -21,6 +21,23 @@ static void freeproc(struct proc *p); extern char trampoline[]; // trampoline.S + +// Allocate a page for each process's kernel stack. +// Map it high in memory, followed by an invalid +// guard page. +void +proc_mapstacks(pagetable_t kpgtbl) { + struct proc *p; + + for(p = proc; p < &proc[NPROC]; p++) { + char *pa = kalloc(); + if(pa == 0) + panic("kalloc"); + uint64 va = KSTACK((int) (p - proc)); + kvmmap(kpgtbl, va, (uint64)pa, PGSIZE, PTE_R | PTE_W); + } +} + // initialize the proc table at boot time. void procinit(void) @@ -30,18 +47,8 @@ procinit(void) initlock(&pid_lock, "nextpid"); for(p = proc; p < &proc[NPROC]; p++) { initlock(&p->lock, "proc"); - - // Allocate a page for the process's kernel stack. - // Map it high in memory, followed by an invalid - // guard page. - char *pa = kalloc(); - if(pa == 0) - panic("kalloc"); - uint64 va = KSTACK((int) (p - proc)); - kvmmap(va, (uint64)pa, PGSIZE, PTE_R | PTE_W); - p->kstack = va; + p->kstack = KSTACK((int) (p - proc)); } - kvminithart(); } // Must be called with interrupts disabled, diff --git a/kernel/vm.c b/kernel/vm.c index 252d97c..50245ff 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -15,33 +15,45 @@ 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. - */ -void -kvminit() +// Make a direct-map page table for the kernel. +pagetable_t +kvmmake(void) { - kernel_pagetable = (pagetable_t) kalloc(); - memset(kernel_pagetable, 0, PGSIZE); + pagetable_t kpgtbl; + + kpgtbl = (pagetable_t) kalloc(); + memset(kpgtbl, 0, PGSIZE); // uart registers - kvmmap(UART0, UART0, PGSIZE, PTE_R | PTE_W); + kvmmap(kpgtbl, UART0, UART0, PGSIZE, PTE_R | PTE_W); // virtio mmio disk interface - kvmmap(VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W); + kvmmap(kpgtbl, VIRTIO0, VIRTIO0, PGSIZE, PTE_R | PTE_W); // PLIC - kvmmap(PLIC, PLIC, 0x400000, PTE_R | PTE_W); + kvmmap(kpgtbl, PLIC, PLIC, 0x400000, PTE_R | PTE_W); // map kernel text executable and read-only. - kvmmap(KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X); + kvmmap(kpgtbl, KERNBASE, KERNBASE, (uint64)etext-KERNBASE, PTE_R | PTE_X); // map kernel data and the physical RAM we'll make use of. - kvmmap((uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W); + kvmmap(kpgtbl, (uint64)etext, (uint64)etext, PHYSTOP-(uint64)etext, PTE_R | PTE_W); // map the trampoline for trap entry/exit to // the highest virtual address in the kernel. - kvmmap(TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X); + kvmmap(kpgtbl, TRAMPOLINE, (uint64)trampoline, PGSIZE, PTE_R | PTE_X); + + // map kernel stacks + proc_mapstacks(kpgtbl); + + return kpgtbl; +} + +// Initialize the one kernel_pagetable +void +kvminit(void) +{ + kernel_pagetable = kvmmake(); } // Switch h/w page table register to the kernel's page table, @@ -112,9 +124,9 @@ walkaddr(pagetable_t pagetable, uint64 va) // only used when booting. // does not flush TLB or enable paging. void -kvmmap(uint64 va, uint64 pa, uint64 sz, int perm) +kvmmap(pagetable_t kpgtbl, uint64 va, uint64 pa, uint64 sz, int perm) { - if(mappages(kernel_pagetable, va, sz, pa, perm) != 0) + if(mappages(kpgtbl, va, sz, pa, perm) != 0) panic("kvmmap"); } -- cgit v1.2.3 From 05a7db1a0a20187760d8a78ff7badfa4cc7e3314 Mon Sep 17 00:00:00 2001 From: Fumiya Shigemitsu Date: Mon, 21 Oct 2019 21:01:07 +0900 Subject: Fix minor typos --- kernel/vm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/vm.c b/kernel/vm.c index 50245ff..b47f111 100644 --- a/kernel/vm.c +++ b/kernel/vm.c @@ -77,7 +77,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) -- cgit v1.2.3 From 286b2f3c3306a621c415e8c7ce67bc2a6501998a Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Tue, 20 Oct 2020 06:55:51 -0400 Subject: consolewrite does not need cons.lock -- can lead to sleep() with lock held --- kernel/console.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/kernel/console.c b/kernel/console.c index d606ed2..23a2d35 100644 --- a/kernel/console.c +++ b/kernel/console.c @@ -60,14 +60,12 @@ consolewrite(int user_src, uint64 src, int n) { int i; - acquire(&cons.lock); for(i = 0; i < n; i++){ char c; if(either_copyin(&c, user_src, src+i, 1) == -1) break; uartputc(c); } - release(&cons.lock); return i; } -- cgit v1.2.3 From 4df1a265cb994ce9cbf9e7dce88b79b1dd7cc5dd Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Tue, 20 Oct 2020 07:02:44 -0400 Subject: fix uart.c to work with UART_TX_BUF_SIZE == 1 --- kernel/uart.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/uart.c b/kernel/uart.c index d586ea4..3c1dc34 100644 --- a/kernel/uart.c +++ b/kernel/uart.c @@ -42,8 +42,8 @@ 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++] +uint64 uart_tx_w; // write next to uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] +uint64 uart_tx_r; // read next from uart_tx_buf[uar_tx_r % UART_TX_BUF_SIZE] extern volatile int panicked; // from printf.c @@ -94,13 +94,13 @@ uartputc(int c) } while(1){ - if(((uart_tx_w + 1) % UART_TX_BUF_SIZE) == uart_tx_r){ + if(uart_tx_w == uart_tx_r + UART_TX_BUF_SIZE){ // 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; + uart_tx_buf[uart_tx_w % UART_TX_BUF_SIZE] = c; + uart_tx_w += 1; uartstart(); release(&uart_tx_lock); return; @@ -150,8 +150,8 @@ uartstart() return; } - int c = uart_tx_buf[uart_tx_r]; - uart_tx_r = (uart_tx_r + 1) % UART_TX_BUF_SIZE; + int c = uart_tx_buf[uart_tx_r % UART_TX_BUF_SIZE]; + uart_tx_r += 1; // maybe uartputc() is waiting for space in the buffer. wakeup(&uart_tx_r); -- cgit v1.2.3 From 147855e5219e91886e71e796fd51fbc6b1a056d2 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 22 Oct 2020 06:36:36 -0400 Subject: test for closed pipe or killed on every char, not just if pipe full --- kernel/pipe.c | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/kernel/pipe.c b/kernel/pipe.c index 7ed402d..b6eefb9 100644 --- a/kernel/pipe.c +++ b/kernel/pipe.c @@ -76,26 +76,29 @@ pipeclose(struct pipe *pi, int writable) int pipewrite(struct pipe *pi, uint64 addr, int n) { - int i; - char ch; + int i = 0; struct proc *pr = myproc(); acquire(&pi->lock); - for(i = 0; i < n; i++){ - while(pi->nwrite == pi->nread + PIPESIZE){ //DOC: pipewrite-full - if(pi->readopen == 0 || pr->killed){ - release(&pi->lock); - return -1; - } + while(i < n){ + if(pi->readopen == 0 || pr->killed){ + release(&pi->lock); + return -1; + } + if(pi->nwrite == pi->nread + PIPESIZE){ //DOC: pipewrite-full wakeup(&pi->nread); sleep(&pi->nwrite, &pi->lock); + } else { + char ch; + if(copyin(pr->pagetable, &ch, addr + i, 1) == -1) + break; + pi->data[pi->nwrite++ % PIPESIZE] = ch; + i++; } - if(copyin(pr->pagetable, &ch, addr + i, 1) == -1) - break; - pi->data[pi->nwrite++ % PIPESIZE] = ch; } wakeup(&pi->nread); release(&pi->lock); + return i; } -- cgit v1.2.3 From d7c0a1b7a703f7366942092f9cf421b6cd111a36 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Fri, 23 Oct 2020 10:18:30 -0400 Subject: hopefully make writei more correct --- kernel/file.c | 6 +++--- kernel/fs.c | 21 +++++++++++---------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/kernel/file.c b/kernel/file.c index 116eb97..25fa226 100644 --- a/kernel/file.c +++ b/kernel/file.c @@ -166,10 +166,10 @@ filewrite(struct file *f, uint64 addr, int n) iunlock(f->ip); end_op(); - if(r < 0) + if(r != n1){ + // error from writei break; - if(r != n1) - panic("short filewrite"); + } i += r; } ret = (i == n ? n : -1); diff --git a/kernel/fs.c b/kernel/fs.c index 848b2c9..f33553a 100644 --- a/kernel/fs.c +++ b/kernel/fs.c @@ -480,6 +480,9 @@ readi(struct inode *ip, int user_dst, uint64 dst, uint off, uint n) // Caller must hold ip->lock. // If user_src==1, then src is a user virtual address; // otherwise, src is a kernel address. +// Returns the number of bytes successfully written. +// If the return value is less than the requested n, +// there was an error of some kind. int writei(struct inode *ip, int user_src, uint64 src, uint off, uint n) { @@ -496,23 +499,21 @@ writei(struct inode *ip, int user_src, uint64 src, uint off, uint n) m = min(n - tot, BSIZE - off%BSIZE); if(either_copyin(bp->data + (off % BSIZE), user_src, src, m) == -1) { brelse(bp); - n = -1; break; } log_write(bp); brelse(bp); } - if(n > 0){ - if(off > ip->size) - ip->size = off; - // write the i-node back to disk even if the size didn't change - // because the loop above might have called bmap() and added a new - // block to ip->addrs[]. - iupdate(ip); - } + if(off > ip->size) + ip->size = off; - return n; + // write the i-node back to disk even if the size didn't change + // because the loop above might have called bmap() and added a new + // block to ip->addrs[]. + iupdate(ip); + + return tot; } // Directories -- cgit v1.2.3 From af570f582cafe3326116f202ab26d81fe95d320a Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Sun, 1 Nov 2020 11:11:38 -0500 Subject: free proc if kalloc fails --- kernel/proc.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kernel/proc.c b/kernel/proc.c index d847c43..87a81fb 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -116,6 +116,7 @@ found: // Allocate a trapframe page. if((p->trapframe = (struct trapframe *)kalloc()) == 0){ + freeproc(p); release(&p->lock); return 0; } -- cgit v1.2.3 From 8d4fbc6e2a4c8373453f2e1380bb8c6ca8d334c0 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Tue, 3 Nov 2020 15:02:08 -0500 Subject: Frans' proc_lock. --- kernel/proc.c | 109 ++++++++++++++++++---------------------------------------- kernel/proc.h | 2 +- 2 files changed, 34 insertions(+), 77 deletions(-) diff --git a/kernel/proc.c b/kernel/proc.c index 87a81fb..d88e453 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -16,11 +16,14 @@ int nextpid = 1; struct spinlock pid_lock; extern void forkret(void); -static void wakeup1(struct proc *chan); static void freeproc(struct proc *p); extern char trampoline[]; // trampoline.S +// protects parent/child relationships. +// must be held when using p->parent. +// must be acquired before any p->lock. +struct spinlock proc_tree_lock; // Allocate a page for each process's kernel stack. // Map it high in memory, followed by an invalid @@ -45,6 +48,7 @@ procinit(void) struct proc *p; initlock(&pid_lock, "nextpid"); + initlock(&proc_tree_lock, "proc_tree"); for(p = proc; p < &proc[NPROC]; p++) { initlock(&p->lock, "proc"); p->kstack = KSTACK((int) (p - proc)); @@ -113,6 +117,7 @@ allocproc(void) found: p->pid = allocpid(); + p->state = USED; // Allocate a trapframe page. if((p->trapframe = (struct trapframe *)kalloc()) == 0){ @@ -283,8 +288,6 @@ fork(void) } np->sz = p->sz; - np->parent = p; - // copy saved user registers. *(np->trapframe) = *(p->trapframe); @@ -301,35 +304,30 @@ fork(void) pid = np->pid; - np->state = RUNNABLE; + release(&np->lock); + acquire(&proc_tree_lock); + np->parent = p; + release(&proc_tree_lock); + + acquire(&np->lock); + np->state = RUNNABLE; release(&np->lock); return pid; } // Pass p's abandoned children to init. -// Caller must hold p->lock. +// Caller must hold proc_tree_lock. void reparent(struct proc *p) { struct proc *pp; for(pp = proc; pp < &proc[NPROC]; pp++){ - // this code uses pp->parent without holding pp->lock. - // acquiring the lock first could cause a deadlock - // if pp or a child of pp were also in exit() - // and about to try to lock p. if(pp->parent == p){ - // pp->parent can't change between the check and the acquire() - // because only the parent changes it, and we're the parent. - acquire(&pp->lock); pp->parent = initproc; - // we should wake up init here, but that would require - // initproc->lock, which would be a deadlock, since we hold - // the lock on one of init's children (pp). this is why - // exit() always wakes init (before acquiring any locks). - release(&pp->lock); + wakeup(initproc); } } } @@ -359,41 +357,20 @@ exit(int status) end_op(); p->cwd = 0; - // we might re-parent a child to init. we can't be precise about - // waking up init, since we can't acquire its lock once we've - // acquired any other proc lock. so wake up init whether that's - // necessary or not. init may miss this wakeup, but that seems - // harmless. - acquire(&initproc->lock); - wakeup1(initproc); - release(&initproc->lock); - - // grab a copy of p->parent, to ensure that we unlock the same - // parent we locked. in case our parent gives us away to init while - // we're waiting for the parent lock. we may then race with an - // exiting parent, but the result will be a harmless spurious wakeup - // to a dead or wrong process; proc structs are never re-allocated - // as anything else. - acquire(&p->lock); - struct proc *original_parent = p->parent; - release(&p->lock); + acquire(&proc_tree_lock); - // we need the parent's lock in order to wake it up from wait(). - // the parent-then-child rule says we have to lock it first. - acquire(&original_parent->lock); - acquire(&p->lock); // Give any children to init. reparent(p); // Parent might be sleeping in wait(). - wakeup1(original_parent); + wakeup(p->parent); p->xstate = status; p->state = ZOMBIE; - release(&original_parent->lock); + release(&proc_tree_lock); // Jump into the scheduler, never to return. sched(); @@ -409,20 +386,13 @@ wait(uint64 addr) int havekids, pid; struct proc *p = myproc(); - // hold p->lock for the whole time to avoid lost - // wakeups from a child's exit(). - acquire(&p->lock); + acquire(&proc_tree_lock); for(;;){ // Scan through table looking for exited children. havekids = 0; for(np = proc; np < &proc[NPROC]; np++){ - // this code uses np->parent without holding np->lock. - // acquiring the lock first would cause a deadlock, - // since np might be an ancestor, and we already hold p->lock. if(np->parent == p){ - // np->parent can't change between the check and the acquire() - // because only the parent changes it, and we're the parent. acquire(&np->lock); havekids = 1; if(np->state == ZOMBIE){ @@ -436,7 +406,7 @@ wait(uint64 addr) } freeproc(np); release(&np->lock); - release(&p->lock); + release(&proc_tree_lock); return pid; } release(&np->lock); @@ -445,12 +415,12 @@ wait(uint64 addr) // No point waiting if we don't have any children. if(!havekids || p->killed){ - release(&p->lock); + release(&proc_tree_lock); return -1; } // Wait for a child to exit. - sleep(p, &p->lock); //DOC: wait-sleep + sleep(p, &proc_tree_lock); //DOC: wait-sleep } } @@ -563,10 +533,9 @@ sleep(void *chan, struct spinlock *lk) // guaranteed that we won't miss any wakeup // (wakeup locks p->lock), // so it's okay to release lk. - if(lk != &p->lock){ //DOC: sleeplock0 - acquire(&p->lock); //DOC: sleeplock1 - release(lk); - } + + acquire(&p->lock); //DOC: sleeplock1 + release(lk); // Go to sleep. p->chan = chan; @@ -578,10 +547,8 @@ sleep(void *chan, struct spinlock *lk) p->chan = 0; // Reacquire original lock. - if(lk != &p->lock){ - release(&p->lock); - acquire(lk); - } + release(&p->lock); + acquire(lk); } // Wake up all processes sleeping on chan. @@ -592,23 +559,13 @@ wakeup(void *chan) struct proc *p; for(p = proc; p < &proc[NPROC]; p++) { - acquire(&p->lock); - if(p->state == SLEEPING && p->chan == chan) { - p->state = RUNNABLE; + if(p != myproc()){ + acquire(&p->lock); + if(p->state == SLEEPING && p->chan == chan) { + p->state = RUNNABLE; + } + release(&p->lock); } - release(&p->lock); - } -} - -// Wake up p if it is sleeping in wait(); used by exit(). -// Caller must hold p->lock. -static void -wakeup1(struct proc *p) -{ - if(!holding(&p->lock)) - panic("wakeup1"); - if(p->chan == p && p->state == SLEEPING) { - p->state = RUNNABLE; } } diff --git a/kernel/proc.h b/kernel/proc.h index 9c16ea7..e86bde1 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -80,7 +80,7 @@ struct trapframe { /* 280 */ uint64 t6; }; -enum procstate { UNUSED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; +enum procstate { UNUSED, USED, SLEEPING, RUNNABLE, RUNNING, ZOMBIE }; // Per-process state struct proc { -- cgit v1.2.3 From 7dea4b93c82354d5a24e8882860af5a1d92282e6 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Tue, 3 Nov 2020 16:35:20 -0500 Subject: oops --- kernel/proc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/proc.c b/kernel/proc.c index d88e453..54c965d 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -401,7 +401,7 @@ wait(uint64 addr) if(addr != 0 && copyout(p->pagetable, addr, (char *)&np->xstate, sizeof(np->xstate)) < 0) { release(&np->lock); - release(&p->lock); + release(&proc_tree_lock); return -1; } freeproc(np); -- cgit v1.2.3 From bc943c230960c414fd43b93317c3825f1a99c611 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 5 Nov 2020 07:32:10 -0500 Subject: don't over-lock in exit() --- kernel/proc.c | 6 ++++-- kernel/proc.h | 4 +++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/kernel/proc.c b/kernel/proc.c index 54c965d..bd77dad 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -358,14 +358,14 @@ exit(int status) p->cwd = 0; acquire(&proc_tree_lock); - - acquire(&p->lock); // Give any children to init. reparent(p); // Parent might be sleeping in wait(). wakeup(p->parent); + + acquire(&p->lock); p->xstate = status; p->state = ZOMBIE; @@ -393,7 +393,9 @@ wait(uint64 addr) havekids = 0; for(np = proc; np < &proc[NPROC]; np++){ if(np->parent == p){ + // make sure the child isn't still in exit() or swtch(). acquire(&np->lock); + havekids = 1; if(np->state == ZOMBIE){ // Found one. diff --git a/kernel/proc.h b/kernel/proc.h index e86bde1..8e90008 100644 --- a/kernel/proc.h +++ b/kernel/proc.h @@ -88,12 +88,14 @@ struct proc { // p->lock must be held when using these: enum procstate state; // Process state - struct proc *parent; // Parent process void *chan; // If non-zero, sleeping on chan int killed; // If non-zero, have been killed int xstate; // Exit status to be returned to parent's wait int pid; // Process ID + // proc_tree_lock must be held when using this: + struct proc *parent; // Parent process + // these are private to the process, so p->lock need not be held. uint64 kstack; // Virtual address of kernel stack uint64 sz; // Size of process memory (bytes) -- cgit v1.2.3 From 774288e05a5891a4de0a165e2a65665714f6ca34 Mon Sep 17 00:00:00 2001 From: Robert Morris Date: Thu, 5 Nov 2020 09:47:59 -0500 Subject: proc_tree_lock -> wait_lock --- kernel/proc.c | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/kernel/proc.c b/kernel/proc.c index bd77dad..22e7ce4 100644 --- a/kernel/proc.c +++ b/kernel/proc.c @@ -20,10 +20,11 @@ static void freeproc(struct proc *p); extern char trampoline[]; // trampoline.S -// protects parent/child relationships. -// must be held when using p->parent. +// helps ensure that wakeups of wait()ing +// parents are not lost. helps obey the +// memory model when using p->parent. // must be acquired before any p->lock. -struct spinlock proc_tree_lock; +struct spinlock wait_lock; // Allocate a page for each process's kernel stack. // Map it high in memory, followed by an invalid @@ -48,7 +49,7 @@ procinit(void) struct proc *p; initlock(&pid_lock, "nextpid"); - initlock(&proc_tree_lock, "proc_tree"); + initlock(&wait_lock, "wait_lock"); for(p = proc; p < &proc[NPROC]; p++) { initlock(&p->lock, "proc"); p->kstack = KSTACK((int) (p - proc)); @@ -306,9 +307,9 @@ fork(void) release(&np->lock); - acquire(&proc_tree_lock); + acquire(&wait_lock); np->parent = p; - release(&proc_tree_lock); + release(&wait_lock); acquire(&np->lock); np->state = RUNNABLE; @@ -318,7 +319,7 @@ fork(void) } // Pass p's abandoned children to init. -// Caller must hold proc_tree_lock. +// Caller must hold wait_lock. void reparent(struct proc *p) { @@ -357,7 +358,7 @@ exit(int status) end_op(); p->cwd = 0; - acquire(&proc_tree_lock); + acquire(&wait_lock); // Give any children to init. reparent(p); @@ -370,7 +371,7 @@ exit(int status) p->xstate = status; p->state = ZOMBIE; - release(&proc_tree_lock); + release(&wait_lock); // Jump into the scheduler, never to return. sched(); @@ -386,7 +387,7 @@ wait(uint64 addr) int havekids, pid; struct proc *p = myproc(); - acquire(&proc_tree_lock); + acquire(&wait_lock); for(;;){ // Scan through table looking for exited children. @@ -403,12 +404,12 @@ wait(uint64 addr) if(addr != 0 && copyout(p->pagetable, addr, (char *)&np->xstate, sizeof(np->xstate)) < 0) { release(&np->lock); - release(&proc_tree_lock); + release(&wait_lock); return -1; } freeproc(np); release(&np->lock); - release(&proc_tree_lock); + release(&wait_lock); return pid; } release(&np->lock); @@ -417,12 +418,12 @@ wait(uint64 addr) // No point waiting if we don't have any children. if(!havekids || p->killed){ - release(&proc_tree_lock); + release(&wait_lock); return -1; } // Wait for a child to exit. - sleep(p, &proc_tree_lock); //DOC: wait-sleep + sleep(p, &wait_lock); //DOC: wait-sleep } } -- cgit v1.2.3 From b63d3506e9fbe7db1cd117db746fb58b7712ab4f Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Thu, 5 Nov 2020 11:09:21 -0500 Subject: Be principled: acquire lock first --- kernel/log.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/log.c b/kernel/log.c index c8af5bd..2c0063a 100644 --- a/kernel/log.c +++ b/kernel/log.c @@ -216,12 +216,12 @@ log_write(struct buf *b) { int i; + acquire(&log.lock); if (log.lh.n >= LOGSIZE || log.lh.n >= log.size - 1) panic("too big a transaction"); if (log.outstanding < 1) panic("log_write outside of trans"); - acquire(&log.lock); for (i = 0; i < log.lh.n; i++) { if (log.lh.block[i] == b->blockno) // log absorbtion break; -- cgit v1.2.3 From d7b308fe81b9fdee1f6060f847b37a252214266a Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Thu, 5 Nov 2020 17:32:35 -0500 Subject: kill/status test --- user/usertests.c | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/user/usertests.c b/user/usertests.c index ec6630d..9be687a 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -779,6 +779,36 @@ pipe1(char *s) } } + +// test if child is killed (status = -1) +void +killstatus(char *s) +{ + int xst; + + for(int i = 0; i < 100; i++){ + int pid1 = fork(); + if(pid1 < 0){ + printf("%s: fork failed\n", s); + exit(1); + } + if(pid1 == 0){ + for (int j = 0; j < 1000; j++) { + getpid(); + } + exit(0); + } + sleep(1); + kill(pid1); + wait(&xst); + if(xst != -1) { + printf("%s: status should be -1\n", s); + exit(1); + } + } + exit(0); +} + // meant to be run w/ at most two CPUs void preempt(char *s) @@ -2786,6 +2816,7 @@ main(int argc, char *argv[]) {iputtest, "iput"}, {mem, "mem"}, {pipe1, "pipe1"}, + {killstatus, "killstatus"}, {preempt, "preempt"}, {exitwait, "exitwait"}, {rmdot, "rmdot"}, -- cgit v1.2.3 From 9599a8e616dc5a1aa6b0c3b7c0c6ed9ffdf5e67a Mon Sep 17 00:00:00 2001 From: Frans Kaashoek Date: Thu, 5 Nov 2020 19:00:37 -0500 Subject: x --- user/usertests.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/user/usertests.c b/user/usertests.c index 9be687a..ba4255b 100644 --- a/user/usertests.c +++ b/user/usertests.c @@ -793,7 +793,7 @@ killstatus(char *s) exit(1); } if(pid1 == 0){ - for (int j = 0; j < 1000; j++) { + while(1) { getpid(); } exit(0); -- cgit v1.2.3