diff --git a/Makefile b/Makefile index a476cf4..9a6b02d 100644 --- a/Makefile +++ b/Makefile @@ -67,6 +67,10 @@ usertests : usertests.o $(ULIB) $(LD) -N -e main -Ttext 0 -o usertests usertests.o $(ULIB) $(OBJDUMP) -S usertests > usertests.asm +fstests : fstests.o $(ULIB) + $(LD) -N -e main -Ttext 0 -o fstests fstests.o $(ULIB) + $(OBJDUMP) -S fstests > fstests.asm + echo : echo.o $(ULIB) $(LD) -N -e main -Ttext 0 -o echo echo.o $(ULIB) $(OBJDUMP) -S echo > echo.asm @@ -102,12 +106,12 @@ rm : rm.o $(ULIB) mkfs : mkfs.c fs.h cc -o mkfs mkfs.c -fs.img : mkfs userfs usertests echo cat readme init sh ls mkdir rm - ./mkfs fs.img userfs usertests echo cat readme init sh ls mkdir rm +fs.img : mkfs userfs usertests echo cat readme init sh ls mkdir rm fstests + ./mkfs fs.img userfs usertests echo cat readme init sh ls mkdir rm fstests -include *.d clean : rm -f *.o *.d *.asm vectors.S parport.out \ bootblock kernel xv6.img user1 userfs usertests \ - fs.img mkfs echo init + fs.img mkfs echo init fstests diff --git a/Notes b/Notes index ab28ec3..dd81f1b 100644 --- a/Notes +++ b/Notes @@ -22,32 +22,14 @@ no kernel malloc(), just kalloc() for user core user pointers aren't valid in the kernel -setting up first process - we do want a process zero, as template - but not runnable - just set up return-from-trap frame on new kernel stack - fake user program that calls exec - -map text read-only? -shared text? - -what's on the stack during a trap or sys call? - PUSHA before scheduler switch? for callee-saved registers. - segment contents? - what does iret need to get out of the kernel? - how does INT know what kernel stack to use? - -are interrupts turned on in the kernel? probably. - -per-cpu curproc -one tss per process, or one per cpu? -one segment array per cpu, or per process? +are interrupts turned on in the kernel? yes. pass curproc explicitly, or implicit from cpu #? e.g. argument to newproc()? hmm, you need a global curproc[cpu] for trap() &c -test stack expansion +no stack expansion + test running out of memory, process slots we can't really use a separate stack segment, since stack addresses @@ -56,16 +38,6 @@ data vs text. how can we have a gap between data and stack, so that both can grow, without committing 4GB of physical memory? does this mean we need paging? -what's the simplest way to add the paging we need? - one page table, re-write it each time we leave the kernel? - page table per process? - probably need to use 0-0xffffffff segments, so that - both data and stack pointers always work - so is it now worth it to make a process's phys mem contiguous? - or could use segment limits and 4 meg pages? - but limits would prevent using stack pointers as data pointers - how to write-protect text? not important? - perhaps have fixed-size stack, put it in the data segment? oops, if kernel stack is in contiguous user phys mem, then moving @@ -87,19 +59,6 @@ test children being inherited by grandparent &c some sleep()s should be interruptible by kill() -cli/sti in acquire/release should nest! - in case you acquire two locks - -what would need fixing if we got rid of kernel_lock? - console output - proc_exit() needs lock on proc *array* to deallocate - kill() needs lock on proc *array* - allocator's free list - global fd table (really free-ness) - sys_close() on fd table - fork on proc list, also next pid - hold lock until public slots in proc struct initialized - locks init_lock sequences CPU startup @@ -110,37 +69,17 @@ locks memory allocator printf -wakeup needs proc_table_lock - so we need recursive locks? - or you must hold the lock to call wakeup? - in general, the table locks protect both free-ness and public variables of table elements in many cases you can use table elements w/o a lock e.g. if you are the process, or you are using an fd -lock code shouldn't call cprintf... - -nasty hack to allow locks before first process, - and to allow them in interrupts when curproc may be zero - -race between release and sleep in sys_wait() -race between sys_exit waking up parent and setting state=ZOMBIE -race in pipe code when full/empty - lock order per-pipe lock proc_table_lock fd_table_lock kalloc_lock console_lock -condition variable + mutex that protects it - proc * (for wait()), proc_table_lock - pipe structure, pipe lock - -systematic way to test sleep races? - print something at the start of sleep? - -do you have to be holding the mutex in order to call wakeup()? +do you have to be holding the mutex in order to call wakeup()? yes device interrupts don't clear FL_IF so a recursive timer interrupt is possible @@ -156,202 +95,11 @@ inode->count counts in-memory pointers to the struct blocks and inodes have ad-hoc sleep-locks provide a single mechanism? -need to lock bufs in bio between bread and brelse - test 14-character file names and file arguments longer than 14 -and directories longer than one sector kalloc() can return 0; do callers handle this right? -why directing interrupts to cpu 1 causes trouble - cpu 1 turns on interrupts with no tss! - and perhaps a stale gdt (from boot) - since it has never run a process, never called setupsegs() - but does cpu really need the tss? - not switching stacks - fake process per cpu, just for tss? - seems like a waste - move tss to cpu[]? - but tss points to per-process kernel stack - would also give us a gdt - OOPS that wasn't the problem - -wait for other cpu to finish starting before enabling interrupts? - some kind of crash in ide_init ioapic_enable cprintf -move ide_init before mp_start? - didn't do any good - maybe cpu0 taking ide interrupt, cpu1 getting a nested lock error - -cprintfs are screwed up if locking is off - often loops forever - hah, just use lpt alone - -looks like cpu0 took the ide interrupt and was the last to hold -the lock, but cpu1 thinks it is nested -cpu0 is in load_icode / printf / cons_putc - probably b/c cpu1 cleared use_console_lock -cpu1 is in scheduler() / printf / acquire - - 1: init timer - 0: init timer - cpu 1 initial nlock 1 - ne0s:t iidd el_occnkt rc - onsole cpu 1 old caller stack 1001A5 10071D 104DFF 1049FE - panic: acquire - ^CNext at t=33002418 - (0) [0x00100091] 0008:0x00100091 (unk. ctxt): jmp .+0xfffffffe ; ebfe - (1) [0x00100332] 0008:0x00100332 (unk. ctxt): jmp .+0xfffffffe - -why is output interleaved even before panic? - -does release turn on interrupts even inside an interrupt handler? - -overflowing cpu[] stack? - probably not, change from 512 to 4096 didn't do anything - - - 1: init timer - 0: init timer - cnpeus te11 linnitki aclo nnoolleek cp1u - ss oarltd sccahleldeul esrt aocnk cpu 0111 Ej6 buf1 01A3140 C5118 - 0 - la anic1::7 0a0c0 uuirr e - ^CNext at t=31691050 - (0) [0x00100373] 0008:0x00100373 (unk. ctxt): jmp .+0xfffffffe ; ebfe - (1) [0x00100091] 0008:0x00100091 (unk. ctxt): jmp .+0xfffffffe ; ebfe - -cpu0: - -0: init timer -nested lock console cpu 0 old caller stack 1001e6 101a34 1 0 - (that's mpmain) -panic: acquire - -cpu1: - -1: init timer -cpu 1 initial nlock 1 -start scheduler on cpu 1 jmpbuf ... -la 107000 lr ... - that is, nlock != 0 - -maybe a race; acquire does - locked = 1 - cpu = cpu() -what if another acquire calls holding w/ locked = 1 but - before cpu is set? - -if I type a lot (kbd), i get a panic -cpu1 in scheduler: panic "holding locks in scheduler" -cpu0 also in the same panic! -recursive interrupt? - FL_IF is probably set during interrupt... is that correct? -again: - olding locks in scheduler - trap v 33 eip 100ED3 c (that is, interrupt while holding a lock) - 100ed3 is in lapic_write -again: - trap v 33 eip 102A3C cpu 1 nlock 1 (in acquire) - panic: interrupt while holding a lock -again: - trap v 33 eip 102A3C cpu 1 nlock 1 - panic: interrupt while holding a lock -OR is it the cprintf("kbd overflow")? - no, get panic even w/o that cprintf -OR a release() at interrupt time turns interrupts back on? - of course i don't think they were off... -OK, fixing trap.c to make interrupts turn off FL_IF - that makes it take longer, but still panics - (maybe b/c release sets FL_IF) - -shouldn't something (PIC?) prevent recursive interrupts of same IRQ? - or should FL_IF be clear during all interrupts? - -maybe acquire should remember old FL_IF value, release should restore - if acquire did cli() - -DUH the increment of nlock in acquire() happens before the cli! - so the panic is probably not a real problem - test nlock, cli(), then increment? - -BUT now userfs doesn't do the final cat README - -AND w/ cprintf("kbd overflow"), panic holding locks in scheduler - maybe also simulataneous panic("interrupt while holding a lock") - -again (holding down x key): - kbd overflow - kbd oaaniicloowh - olding locks in scheduler - trap v 33 eip 100F5F c^CNext at t=32166285 - (0) [0x0010033e] 0008:0010033e (unk. ctxt): jmp .+0xfffffffe (0x0010033e) ; ebfe - (1) [0x0010005c] 0008:0010005c (unk. ctxt): jmp .+0xfffffffe (0x0010005c) ; ebfe -cpu0 paniced due to holding locks in scheduler -cpu1 got panic("interrupt while holding a lock") - again in lapic_write. - while re-enabling an IRQ? - -again: -cpu 0 panic("holding locks in scheduler") - but didn't trigger related panics earlier in scheduler or sched() - of course the panic is right after release() and thus sti() - so we may be seeing an interrupt that left locks held -cpu 1 unknown panic -why does it happen to both cpus at the same time? - -again: -cpu 0 panic("holding locks in scheduler") - but trap() didn't see any held locks on return -cpu 1 no apparent panic - -again: -cpu 0 panic: holding too many locks in scheduler -cpu 1 panic: kbd_intr returned while holding a lock - -again: -cpu 0 panic: holding too man - la 10d70c lr 10027b - those don't seem to be locks... - only place non-constant lock is used is sleep()'s 2nd arg - maybe register not preserved across context switch? - it's in %esi... - sched() doesn't touch %esi - %esi is evidently callee-saved - something to do with interrupts? since ordinarily it works -cpu 1 panic: kbd_int returned while holding a lock - la 107340 lr 107300 - console_lock and kbd_lock - -maybe console_lock is often not released due to change - in use_console_lock (panic on other cpu) - -again: -cpu 0: panic: h... - la 10D78C lr 102CA0 -cpu 1: panic: acquire FL_IF (later than cpu 0) - -but if sleep() were acquiring random locks, we'd see panics -in release, after sleep() returned. -actually when system is idle, maybe no-one sleeps at all. - just scheduler() and interrupts - -questions: - does userfs use pipes? or fork? - no - does anything bad happen if process 1 exits? eg exit() in cat.c - looks ok - are there really no processes left? - lock_init() so we can have a magic number? - -HMM maybe the variables at the end of struct cpu are being overwritten - nlocks, lastacquire, lastrelease - by cpu->stack? - adding junk buffers maybe causes crash to take longer... - when do we run on cpu stack? - just in scheduler()? - and interrupts from scheduler() - OH! recursive interrupts will use up any amount of cpu[].stack! underflow and wrecks *previous* cpu's struct @@ -360,15 +108,26 @@ mkdir sh arguments sh redirection indirect blocks -two bugs in unlink: don't just return if nlink > 0, - and search for name, not inum is there a create/create race for same file name? resulting in two entries w/ same name in directory? +why does shell often ignore first line of input? test: one process unlinks a file while another links to it -test: simultaneous create of same file test: one process opens a file while another deletes it +test: mkdir. deadlock d/.. vs ../d -wdir should use writei, to avoid special-case block allocation - also readi - is dir locked? probably +make proc[0] runnable +cpu early tss and gdt +how do we get cpu0 scheduler() to use mpstack, not proc[0].kstack? +when iget() first sleeps, where does it longjmp to? +maybe set up proc[0] to be runnable, with entry proc0main(), then + have main() call scheduler()? + perhaps so proc[0] uses right kstack? + and scheduler() uses mpstack? +ltr sets the busy bit in the TSS, faults if already set + so gdt and TSS per cpu? + we don't want to be using some random process's gdt when it changes it. +maybe get rid of per-proc gdt and ts + one per cpu + refresh it when needed + setupsegs(proc *) diff --git a/fstests.c b/fstests.c new file mode 100644 index 0000000..d6f630e --- /dev/null +++ b/fstests.c @@ -0,0 +1,380 @@ +#include "user.h" +#include "fcntl.h" + +char buf[512]; + +// two processes write to the same file descriptor +// is the offset shared? does inode locking work? +void +sharedfd() +{ + int fd, pid, i, n, nc, np; + char buf[10]; + + unlink("sharedfd"); + fd = open("sharedfd", O_CREATE|O_RDWR); + if(fd < 0){ + printf(1, "fstests: cannot open sharedfd for writing"); + return; + } + pid = fork(); + memset(buf, pid==0?'c':'p', sizeof(buf)); + for(i = 0; i < 100; i++){ + if(write(fd, buf, sizeof(buf)) != sizeof(buf)){ + printf(1, "fstests: write sharedfd failed\n"); + break; + } + } + if(pid == 0) + exit(); + else + wait(); + close(fd); + fd = open("sharedfd", 0); + if(fd < 0){ + printf(1, "fstests: cannot open sharedfd for reading\n"); + return; + } + nc = np = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(i = 0; i < sizeof(buf); i++){ + if(buf[i] == 'c') + nc++; + if(buf[i] == 'p') + np++; + } + } + close(fd); + unlink("sharedfd"); + if(nc == 1000 && np == 1000) + printf(1, "sharedfd ok\n"); + else + printf(1, "sharedfd oops %d %d\n", nc, np); +} + +// two processes write two different files at the same +// time, to test block allocation. +void +twofiles() +{ + int fd, pid, i, j, n, total; + char *fname; + + unlink("f1"); + unlink("f2"); + + pid = fork(); + if(pid < 0){ + puts("fork failed\n"); + return; + } + + fname = pid ? "f1" : "f2"; + fd = open(fname, O_CREATE | O_RDWR); + if(fd < 0){ + puts("create failed\n"); + exit(); + } + + memset(buf, pid?'p':'c', 512); + for(i = 0; i < 12; i++){ + if((n = write(fd, buf, 500)) != 500){ + printf(1, "write failed %d\n", n); + exit(); + } + } + close(fd); + if(pid) + wait(); + else + exit(); + + for(i = 0; i < 2; i++){ + fd = open(i?"f1":"f2", 0); + total = 0; + while((n = read(fd, buf, sizeof(buf))) > 0){ + for(j = 0; j < n; j++){ + if(buf[j] != (i?'p':'c')){ + puts("wrong char\n"); + exit(); + } + } + total += n; + } + close(fd); + if(total != 12*500){ + printf(1, "wrong length %d\n", total); + exit(); + } + } + + unlink("f1"); + unlink("f2"); + + puts("twofiles ok\n"); +} + +// two processes create and delete files in same directory +void +createdelete() +{ + int pid, i, fd; + int n = 20; + char name[32]; + + pid = fork(); + if(pid < 0){ + puts("fork failed\n"); + exit(); + } + + name[0] = pid ? 'p' : 'c'; + name[2] = '\0'; + for(i = 0; i < n; i++){ + name[1] = '0' + i; + fd = open(name, O_CREATE | O_RDWR); + if(fd < 0){ + puts("create failed\n"); + exit(); + } + close(fd); + if(i > 0 && (i % 2 ) == 0){ + name[1] = '0' + (i / 2); + if(unlink(name) < 0){ + puts("unlink failed\n"); + exit(); + } + } + } + + if(pid) + wait(); + else + exit(); + + for(i = 0; i < n; i++){ + name[0] = 'p'; + name[1] = '0' + i; + fd = open(name, 0); + if((i == 0 || i >= n/2) && fd < 0){ + printf(1, "oops createdelete %s didn't exist\n", name); + } else if((i >= 1 && i < n/2) && fd >= 0){ + printf(1, "oops createdelete %s did exist\n", name); + } + if(fd >= 0) + close(fd); + + name[0] = 'c'; + name[1] = '0' + i; + fd = open(name, 0); + if((i == 0 || i >= n/2) && fd < 0){ + printf(1, "oops createdelete %s didn't exist\n", name); + } else if((i >= 1 && i < n/2) && fd >= 0){ + printf(1, "oops createdelete %s did exist\n", name); + } + if(fd >= 0) + close(fd); + } + + for(i = 0; i < n; i++){ + name[0] = 'p'; + name[1] = '0' + i; + unlink(name); + name[0] = 'c'; + unlink(name); + } + + printf(1, "createdelete ok\n"); +} + +// can I unlink a file and still read it? +void +unlinkread() +{ + int fd, fd1; + + fd = open("unlinkread", O_CREATE | O_RDWR); + if(fd < 0){ + puts("create unlinkread failed\n"); + exit(); + } + write(fd, "hello", 5); + close(fd); + + fd = open("unlinkread", O_RDWR); + if(fd < 0){ + puts("open unlinkread failed\n"); + exit(); + } + if(unlink("unlinkread") != 0){ + puts("unlink unlinkread failed\n"); + exit(); + } + + fd1 = open("xxx", O_CREATE | O_RDWR); + write(fd1, "yyy", 3); + close(fd1); + + if(read(fd, buf, sizeof(buf)) != 5){ + puts("unlinkread read failed"); + exit(); + } + if(buf[0] != 'h'){ + puts("unlinkread wrong data\n"); + exit(); + } + if(write(fd, buf, 10) != 10){ + puts("unlinkread write failed\n"); + exit(); + } + close(fd); + unlink("xxx"); + puts("unlinkread ok\n"); +} + +void +linktest() +{ + int fd; + + unlink("lf1"); + unlink("lf2"); + + fd = open("lf1", O_CREATE|O_RDWR); + if(fd < 0){ + puts("create lf1 failed\n"); + exit(); + } + if(write(fd, "hello", 5) != 5){ + puts("write lf1 failed\n"); + exit(); + } + close(fd); + + if(link("lf1", "lf2") < 0){ + puts("link lf1 lf2 failed\n"); + exit(); + } + unlink("lf1"); + + if(open("lf1", 0) >= 0){ + puts("unlinked lf1 but it is still there!\n"); + exit(); + } + + fd = open("lf2", 0); + if(fd < 0){ + puts("open lf2 failed\n"); + exit(); + } + if(read(fd, buf, sizeof(buf)) != 5){ + puts("read lf2 failed\n"); + exit(); + } + close(fd); + + if(link("lf2", "lf2") >= 0){ + puts("link lf2 lf2 succeeded! oops\n"); + exit(); + } + + unlink("lf2"); + if(link("lf2", "lf1") >= 0){ + puts("link non-existant succeeded! oops\n"); + exit(); + } + + if(link(".", "lf1") >= 0){ + puts("link . lf1 succeeded! oops\n"); + exit(); + } + + puts("linktest ok\n"); +} + +// test concurrent create of the same file +void +concreate() +{ + char file[3]; + int i, pid, n, fd; + char fa[40]; + struct { + unsigned short inum; + char name[14]; + } de; + + file[0] = 'C'; + file[2] = '\0'; + for(i = 0; i < 40; i++){ + file[1] = '0' + i; + unlink(file); + pid = fork(); + if(pid && (i % 3) == 1){ + link("C0", file); + } else if(pid == 0 && (i % 5) == 1){ + link("C0", file); + } else { + fd = open(file, O_CREATE | O_RDWR); + if(fd < 0){ + puts("concreate create failed\n"); + exit(); + } + close(fd); + } + if(pid == 0) + exit(); + else + wait(); + } + + memset(fa, 0, sizeof(fa)); + fd = open(".", 0); + n = 0; + while(read(fd, &de, sizeof(de)) > 0){ + if(de.inum == 0) + continue; + if(de.name[0] == 'C' && de.name[2] == '\0'){ + i = de.name[1] - '0'; + if(i < 0 || i >= sizeof(fa)){ + printf(1, "concreate weird file %s\n", de.name); + exit(); + } + if(fa[i]){ + printf(1, "concreate duplicate file %s\n", de.name); + exit(); + } + fa[i] = 1; + n++; + } + } + close(fd); + + if(n != 40){ + puts("concreate not enough files in directory listing\n"); + exit(); + } + + for(i = 0; i < 40; i++){ + file[1] = '0' + i; + unlink(file); + } + + puts("concreate ok\n"); +} + +int +main(int argc, char *argv[]) +{ + puts("fstests starting\n"); + + concreate(); + linktest(); + unlinkread(); + createdelete(); + twofiles(); + sharedfd(); + + puts("fstests finished\n"); + exit(); +} diff --git a/main.c b/main.c index d877208..35ea672 100644 --- a/main.c +++ b/main.c @@ -29,10 +29,14 @@ main0(void) // clear BSS memset(edata, 0, end - edata); + // switch to cpu0's cpu stack + asm volatile("movl %0, %%esp" : : "r" (cpus[0].mpstack + MPSTACK - 32)); + asm volatile("movl %0, %%ebp" : : "r" (cpus[0].mpstack + MPSTACK)); + // Make sure interrupts stay disabled on all processors // until each signals it is ready, by pretending to hold // an extra lock. - // xxx maybe replace w/ acquire remembering if FL_IF + // xxx maybe replace w/ acquire remembering if FL_IF was already clear for(i=0; istate = IDLEPROC; - p->kstack = cpus[p-proc].mpstack; - p->pid = p - proc; - } - // fix process 0 so that copyproc() will work p = &proc[0]; + p->state = IDLEPROC; p->sz = 4 * PAGE; p->mem = kalloc(p->sz); memset(p->mem, 0, p->sz); @@ -74,8 +71,9 @@ main0(void) p->tf->es = p->tf->ds = p->tf->ss = (SEG_UDATA << 3) | 3; p->tf->cs = (SEG_UCODE << 3) | 3; p->tf->eflags = FL_IF; - setupsegs(p); - // curproc[cpu()] = p; + + // make sure there's a TSS + setupsegs(0); // initialize I/O devices, let them enable interrupts console_init(); @@ -117,7 +115,8 @@ mpmain(void) lapic_timerinit(); lapic_enableintr(); - setupsegs(&proc[cpu()]); + // make sure there's a TSS + setupsegs(0); cpuid(0, 0, 0, 0, 0); // memory barrier cpus[cpu()].booted = 1; diff --git a/proc.c b/proc.c index 6880fd4..a7908e2 100644 --- a/proc.c +++ b/proc.c @@ -11,7 +11,7 @@ struct spinlock proc_table_lock; struct proc proc[NPROC]; struct proc *curproc[NCPU]; -int next_pid = NCPU; +int next_pid = 1; extern void forkret(void); extern void forkret1(struct trapframe*); @@ -22,28 +22,39 @@ pinit(void) } /* - * set up a process's task state and segment descriptors - * correctly, given its current size and address in memory. - * this should be called whenever the latter change. - * doesn't change the cpu's current segmentation setup. + * set up CPU's segment descriptors and task state for a + * given process. If p==0, set up for "idle" state for + * when scheduler() isn't running any process. */ void setupsegs(struct proc *p) { - memset(&p->ts, 0, sizeof(struct taskstate)); - p->ts.ss0 = SEG_KDATA << 3; - p->ts.esp0 = (uint)(p->kstack + KSTACKSIZE); + struct cpu *c = &cpus[cpu()]; + + c->ts.ss0 = SEG_KDATA << 3; + if(p){ + c->ts.esp0 = (uint)(p->kstack + KSTACKSIZE); + } else { + c->ts.esp0 = 0xffffffff; + } // XXX it may be wrong to modify the current segment table! - p->gdt[0] = SEG_NULL; - p->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0x100000 + 64*1024, 0); // xxx - p->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0); - p->gdt[SEG_TSS] = SEG16(STS_T32A, (uint) &p->ts, - sizeof(p->ts), 0); - p->gdt[SEG_TSS].s = 0; - p->gdt[SEG_UCODE] = SEG(STA_X|STA_R, (uint)p->mem, p->sz, 3); - p->gdt[SEG_UDATA] = SEG(STA_W, (uint)p->mem, p->sz, 3); + c->gdt[0] = SEG_NULL; + c->gdt[SEG_KCODE] = SEG(STA_X|STA_R, 0, 0x100000 + 64*1024, 0); // xxx + c->gdt[SEG_KDATA] = SEG(STA_W, 0, 0xffffffff, 0); + c->gdt[SEG_TSS] = SEG16(STS_T32A, (uint) &c->ts, sizeof(c->ts), 0); + c->gdt[SEG_TSS].s = 0; + if(p){ + c->gdt[SEG_UCODE] = SEG(STA_X|STA_R, (uint)p->mem, p->sz, 3); + c->gdt[SEG_UDATA] = SEG(STA_W, (uint)p->mem, p->sz, 3); + } else { + c->gdt[SEG_UCODE] = SEG_NULL; + c->gdt[SEG_UDATA] = SEG_NULL; + } + + lgdt(c->gdt, sizeof c->gdt); + ltr(SEG_TSS << 3); } // Look in the process table for an UNUSED proc. @@ -101,9 +112,6 @@ copyproc(struct proc* p) np->state = UNUSED; return 0; } - - // Initialize segment table. - setupsegs(np); // Copy trapframe registers from parent. np->tf = (struct trapframe*)(np->kstack + KSTACKSIZE) - 1; @@ -159,26 +167,11 @@ scheduler(void) if(p->state != RUNNABLE) continue; - // Run this process. - // XXX move this into swtch or trapret or something. - // It can run on the other stack. - // h/w sets busy bit in TSS descriptor sometimes, and faults - // if it's set in LTR. so clear tss descriptor busy bit. - p->gdt[SEG_TSS].type = STS_T32A; - - // XXX should probably have an lgdt() function in x86.h - // to confine all the inline assembly. - // XXX probably ought to lgdt on trap return too, in case - // a system call has moved a program or changed its size. - lgdt(p->gdt, sizeof p->gdt); - // asm volatile("lgdt %0" : : "g" (p->gdt_pd.lim)); - - ltr(SEG_TSS << 3); - // Switch to chosen process. It is the process's job // to release proc_table_lock and then reacquire it // before jumping back to us. - if(0) cprintf("cpu%d: run %d\n", cpu(), p-proc); + + setupsegs(p); curproc[cpu()] = p; p->state = RUNNING; if(setjmp(&cpus[cpu()].jmpbuf) == 0) @@ -199,9 +192,7 @@ scheduler(void) panic("scheduler lock"); } - setupsegs(&proc[cpu()]); - - // XXX if not holding proc_table_lock panic. + setupsegs(0); } release(&proc_table_lock); diff --git a/proc.h b/proc.h index 88e630b..611f9b5 100644 --- a/proc.h +++ b/proc.h @@ -48,8 +48,6 @@ struct proc{ struct fd *fds[NOFILE]; struct inode *cwd; - struct taskstate ts; // only to give cpu address of kernel stack - struct segdesc gdt[NSEGS]; uint esp; // kernel stack pointer uint ebp; // kernel frame pointer @@ -67,6 +65,8 @@ extern struct proc *curproc[NCPU]; // can be NULL if no proc running. struct cpu { uchar apicid; // Local APIC ID struct jmpbuf jmpbuf; + struct taskstate ts; // only to give cpu address of kernel stack + struct segdesc gdt[NSEGS]; int guard1; char mpstack[MPSTACK]; // per-cpu start-up stack int guard2; diff --git a/trap.c b/trap.c index 99aaa70..2bb3e9e 100644 --- a/trap.c +++ b/trap.c @@ -41,8 +41,8 @@ trap(struct trapframe *tf) panic("interrupt while holding a lock"); } - if(cpu() == 1 && curproc[cpu()] == 0){ - if(&tf < cpus[cpu()].mpstack || &tf > cpus[cpu()].mpstack + 512){ + if(curproc[cpu()] == 0){ + if(&tf < cpus[cpu()].mpstack || &tf > cpus[cpu()].mpstack + MPSTACK){ cprintf("&tf %x mpstack %x\n", &tf, cpus[cpu()].mpstack); panic("trap cpu stack"); } @@ -125,7 +125,8 @@ trap(struct trapframe *tf) return; } - cprintf("trap %d\n", v); + cprintf("trap %d from cpu %d eip %x\n", v, cpu(), tf->eip); + panic("trap"); return; } diff --git a/usertests.c b/usertests.c index c3e9113..20155c2 100644 --- a/usertests.c +++ b/usertests.c @@ -115,305 +115,11 @@ exitwait(void) puts("exitwait ok\n"); } -// two processes write to the same file descriptor -// is the offset shared? does inode locking work? -void -sharedfd() -{ - int fd, pid, i, n, nc, np; - char buf[10]; - - unlink("sharedfd"); - fd = open("sharedfd", O_CREATE|O_RDWR); - if(fd < 0){ - printf(1, "usertests: cannot open sharedfd for writing"); - return; - } - pid = fork(); - memset(buf, pid==0?'c':'p', sizeof(buf)); - for(i = 0; i < 100; i++){ - if(write(fd, buf, sizeof(buf)) != sizeof(buf)){ - printf(1, "usertests: write sharedfd failed\n"); - break; - } - } - if(pid == 0) - exit(); - else - wait(); - close(fd); - fd = open("sharedfd", 0); - if(fd < 0){ - printf(1, "usertests: cannot open sharedfd for reading\n"); - return; - } - nc = np = 0; - while((n = read(fd, buf, sizeof(buf))) > 0){ - for(i = 0; i < sizeof(buf); i++){ - if(buf[i] == 'c') - nc++; - if(buf[i] == 'p') - np++; - } - } - close(fd); - unlink("sharedfd"); - if(nc == 1000 && np == 1000) - printf(1, "sharedfd ok\n"); - else - printf(1, "sharedfd oops %d %d\n", nc, np); -} - -// two processes write two different files at the same -// time, to test block allocation. -void -twofiles() -{ - int fd, pid, i, j, n, total; - char *fname; - - unlink("f1"); - unlink("f2"); - - pid = fork(); - if(pid < 0){ - puts("fork failed\n"); - return; - } - - fname = pid ? "f1" : "f2"; - fd = open(fname, O_CREATE | O_RDWR); - if(fd < 0){ - puts("create failed\n"); - exit(); - } - - memset(buf, pid?'p':'c', 512); - for(i = 0; i < 12; i++){ - if((n = write(fd, buf, 500)) != 500){ - printf(1, "write failed %d\n", n); - exit(); - } - } - close(fd); - if(pid) - wait(); - else - exit(); - - for(i = 0; i < 2; i++){ - fd = open(i?"f1":"f2", 0); - total = 0; - while((n = read(fd, buf, sizeof(buf))) > 0){ - for(j = 0; j < n; j++){ - if(buf[j] != (i?'p':'c')){ - puts("wrong char\n"); - exit(); - } - } - total += n; - } - close(fd); - if(total != 12*500){ - printf(1, "wrong length %d\n", total); - exit(); - } - } - - unlink("f1"); - unlink("f2"); - - puts("twofiles ok\n"); -} - -// two processes create and delete files in same directory -void -createdelete() -{ - int pid, i, fd; - int n = 20; - char name[32]; - - pid = fork(); - if(pid < 0){ - puts("fork failed\n"); - exit(); - } - - name[0] = pid ? 'p' : 'c'; - name[2] = '\0'; - for(i = 0; i < n; i++){ - name[1] = '0' + i; - fd = open(name, O_CREATE | O_RDWR); - if(fd < 0){ - puts("create failed\n"); - exit(); - } - close(fd); - if(i > 0 && (i % 2 ) == 0){ - name[1] = '0' + (i / 2); - if(unlink(name) < 0){ - puts("unlink failed\n"); - exit(); - } - } - } - - if(pid) - wait(); - else - exit(); - - for(i = 0; i < n; i++){ - name[0] = 'p'; - name[1] = '0' + i; - fd = open(name, 0); - if((i == 0 || i >= n/2) && fd < 0){ - printf(1, "oops createdelete %s didn't exist\n", name); - } else if((i >= 1 && i < n/2) && fd >= 0){ - printf(1, "oops createdelete %s did exist\n", name); - } - if(fd >= 0) - close(fd); - - name[0] = 'c'; - name[1] = '0' + i; - fd = open(name, 0); - if((i == 0 || i >= n/2) && fd < 0){ - printf(1, "oops createdelete %s didn't exist\n", name); - } else if((i >= 1 && i < n/2) && fd >= 0){ - printf(1, "oops createdelete %s did exist\n", name); - } - if(fd >= 0) - close(fd); - } - - for(i = 0; i < n; i++){ - name[0] = 'p'; - name[1] = '0' + i; - unlink(name); - name[0] = 'c'; - unlink(name); - } - - printf(1, "createdelete ok\n"); -} - -// can I unlink a file and still read it? -void -unlinkread() -{ - int fd, fd1; - - fd = open("unlinkread", O_CREATE | O_RDWR); - if(fd < 0){ - puts("create unlinkread failed\n"); - exit(); - } - write(fd, "hello", 5); - close(fd); - - fd = open("unlinkread", O_RDWR); - if(fd < 0){ - puts("open unlinkread failed\n"); - exit(); - } - if(unlink("unlinkread") != 0){ - puts("unlink unlinkread failed\n"); - exit(); - } - - fd1 = open("xxx", O_CREATE | O_RDWR); - write(fd1, "yyy", 3); - close(fd1); - - if(read(fd, buf, sizeof(buf)) != 5){ - puts("unlinkread read failed"); - exit(); - } - if(buf[0] != 'h'){ - puts("unlinkread wrong data\n"); - exit(); - } - if(write(fd, buf, 10) != 10){ - puts("unlinkread write failed\n"); - exit(); - } - close(fd); - unlink("xxx"); - puts("unlinkread ok\n"); -} - -void -linktest() -{ - int fd; - - unlink("lf1"); - unlink("lf2"); - - fd = open("lf1", O_CREATE|O_RDWR); - if(fd < 0){ - puts("create lf1 failed\n"); - exit(); - } - if(write(fd, "hello", 5) != 5){ - puts("write lf1 failed\n"); - exit(); - } - close(fd); - - if(link("lf1", "lf2") < 0){ - puts("link lf1 lf2 failed\n"); - exit(); - } - unlink("lf1"); - - if(open("lf1", 0) >= 0){ - puts("unlinked lf1 but it is still there!\n"); - exit(); - } - - fd = open("lf2", 0); - if(fd < 0){ - puts("open lf2 failed\n"); - exit(); - } - if(read(fd, buf, sizeof(buf)) != 5){ - puts("read lf2 failed\n"); - exit(); - } - close(fd); - - if(link("lf2", "lf2") >= 0){ - puts("link lf2 lf2 succeeded! oops\n"); - exit(); - } - - unlink("lf2"); - if(link("lf2", "lf1") >= 0){ - puts("link non-existant succeeded! oops\n"); - exit(); - } - - if(link(".", "lf1") >= 0){ - puts("link . lf1 succeeded! oops\n"); - exit(); - } - - puts("linktest ok\n"); -} - int main(int argc, char *argv[]) { puts("usertests starting\n"); - linktest(); - unlinkread(); - createdelete(); - twofiles(); - sharedfd(); pipe1(); preempt(); exitwait();