// Switch to and run a specified process, which must already be locked. void gcc_noreturn proc_run(proc *p) { // cprintf("proc_run %p\n", p); if(!spinlock_holding(&(p->lock))) panic("should have p->lock.\n"); p->runcpu = cpu_cur(); p->state = PROC_RUN; cpu_cur()->proc = p; spinlock_release(&(p->lock)); trap_return(&(p->sv.tf)); }
void file_initroot(proc *root) { // Only one root process may perform external I/O directly - // all other processes do I/O indirectly via the process hierarchy. assert(root == proc_root); // Make sure the root process's page directory is loaded, // so that we can write into the root process's file area directly. cpu_cur()->proc = root; lcr3(mem_phys(root->pdir)); // Enable read/write access on the file metadata area pmap_setperm(root->pdir, FILESVA, ROUNDUP(sizeof(filestate), PAGESIZE), SYS_READ | SYS_WRITE); memset(files, 0, sizeof(*files)); // Set up the standard I/O descriptors for console I/O files->fd[0].ino = FILEINO_CONSIN; files->fd[0].flags = O_RDONLY; files->fd[1].ino = FILEINO_CONSOUT; files->fd[1].flags = O_WRONLY | O_APPEND; files->fd[2].ino = FILEINO_CONSOUT; files->fd[2].flags = O_WRONLY | O_APPEND; // Setup the inodes for the console I/O files and root directory strcpy(files->fi[FILEINO_CONSIN].de.d_name, "consin"); strcpy(files->fi[FILEINO_CONSOUT].de.d_name, "consout"); strcpy(files->fi[FILEINO_ROOTDIR].de.d_name, "/"); files->fi[FILEINO_CONSIN].dino = FILEINO_ROOTDIR; files->fi[FILEINO_CONSOUT].dino = FILEINO_ROOTDIR; files->fi[FILEINO_ROOTDIR].dino = FILEINO_ROOTDIR; files->fi[FILEINO_CONSIN].mode = S_IFREG | S_IFPART; files->fi[FILEINO_CONSOUT].mode = S_IFREG; files->fi[FILEINO_ROOTDIR].mode = S_IFDIR; // Set the whole console input area to be read/write, // so we won't have to worry about perms in cons_io(). pmap_setperm(root->pdir, (uintptr_t)FILEDATA(FILEINO_CONSIN), PTSIZE, SYS_READ | SYS_WRITE); // Set up the initial files in the root process's file system. // Some script magic in kern/Makefrag creates obj/kern/initfiles.h, // which gets included above (twice) to create the 'initfiles' array. // For each initial file numbered 0 <= i < ninitfiles, // initfiles[i][0] is a pointer to the filename string for that file, // initfiles[i][1] is a pointer to the start of the file's content, and // initfiles[i][2] is a pointer to the end of the file's content // (i.e., a pointer to the first byte after the file's last byte). int ninitfiles = sizeof(initfiles)/sizeof(initfiles[0]); // Lab 4: your file system initialization code here. warn("file_initroot: file system initialization not done\n"); // Set root process's current working directory files->cwd = FILEINO_ROOTDIR; // Child process state - reserve PID 0 as a "scratch" child process. files->child[0].state = PROC_RESERVED; }
// Recover from a trap that occurs during a copyin or copyout, // by aborting the system call and reflecting the trap to the parent process, // behaving as if the user program's INT instruction had caused the trap. // This uses the 'recover' pointer in the current cpu struct, // and invokes systrap() above to blame the trap on the user process. // // Notes: // - Be sure the parent gets the correct trapno, err, and eip values. // - Be sure to release any spinlocks you were holding during the copyin/out. // static void gcc_noreturn sysrecover(trapframe *ktf, void *recoverdata) { trapframe *utf = (trapframe*)recoverdata; cpu *c = cpu_cur(); c->recover = NULL; systrap(utf, ktf->trapno, ktf->err); }
// Acquire the lock. // Loops (spins) until the lock is acquired. // Holding a lock for a long time may cause // other CPUs to waste time spinning to acquire it. void spinlock_acquire(struct spinlock *lk) { if(spinlock_holding(lk)) panic("Already holding lock."); while(xchg(&(lk->locked), 1) != 0) pause(); lk->cpu = cpu_cur(); debug_trace(read_ebp(), lk->eips); }
// Yield the current CPU to another ready process. // Called while handling a timer interrupt. void gcc_noreturn proc_yield(trapframe *tf) { proc *p; p = cpu_cur()->proc; spinlock_acquire(&(p->lock)); proc_save(p, tf, -1); spinlock_release(&(p->lock)); proc_ready(p); proc_sched(); }
// Copy data to/from user space, // using checkva() above to validate the address range // and using sysrecover() to recover from any traps during the copy. void usercopy(trapframe *utf, bool copyout, void *kva, uint32_t uva, size_t size) { checkva(utf, uva, size); cpu *c = cpu_cur(); c->recover = sysrecover; if(copyout) memmove((void*)uva, kva, size); else memmove(kva, (void*)uva, size); c->recover = NULL; }
void spinlock_check() { const int NUMLOCKS=10; const int NUMRUNS=5; int i,j,run; const char* file = "spinlock_check"; spinlock locks[NUMLOCKS]; // Initialize the locks for(i=0;i<NUMLOCKS;i++) spinlock_init_(&locks[i], file, 0); // Make sure that all locks have CPU set to NULL initially for(i=0;i<NUMLOCKS;i++) assert(locks[i].cpu==NULL); // Make sure that all locks have the correct debug info. for(i=0;i<NUMLOCKS;i++) assert(locks[i].file==file); for (run=0;run<NUMRUNS;run++) { // Lock all locks for(i=0;i<NUMLOCKS;i++) spinlock_godeep(i, &locks[i]); // Make sure that all locks have the right CPU for(i=0;i<NUMLOCKS;i++) assert(locks[i].cpu == cpu_cur()); // Make sure that all locks have holding correctly implemented. for(i=0;i<NUMLOCKS;i++) assert(spinlock_holding(&locks[i]) != 0); // Make sure that top i frames are somewhere in godeep. for(i=0;i<NUMLOCKS;i++) { for(j=0; j<=i && j < DEBUG_TRACEFRAMES ; j++) { assert(locks[i].eips[j] >= (uint32_t)spinlock_godeep); assert(locks[i].eips[j] < (uint32_t)spinlock_godeep+100); } } // Release all locks for(i=0;i<NUMLOCKS;i++) spinlock_release(&locks[i]); // Make sure that the CPU has been cleared for(i=0;i<NUMLOCKS;i++) assert(locks[i].cpu == NULL); for(i=0;i<NUMLOCKS;i++) assert(locks[i].eips[0]==0); // Make sure that all locks have holding correctly implemented. for(i=0;i<NUMLOCKS;i++) assert(spinlock_holding(&locks[i]) == 0); } cprintf("spinlock_check() succeeded!\n"); }
// Switch to and run a specified process, which must already be locked. void gcc_noreturn proc_run(proc *p) { if (!spinlock_holding(&p->lock)) panic("proc_run without lock"); //if (p->parent) cprintf("running child\n"); // else cprintf("running root\n"); p->state = PROC_RUN; p->runcpu = cpu_cur(); cpu_cur()->proc = p; // Enable interrupts (for preemption) p->sv.tf.eflags |= (1 << 9); p->sv.tf.ds = CPU_GDT_UDATA | 3; p->sv.tf.es = CPU_GDT_UDATA | 3; p->sv.tf.cs = CPU_GDT_UCODE | 3; p->sv.tf.ss = CPU_GDT_UDATA | 3; lcr3 (mem_phys(p->pdir)); spinlock_release(&p->lock); trap_return(&p->sv.tf); }
// Put the current process to sleep by "returning" to its parent process. // Used both when a process calls the SYS_RET system call explicitly, // and when a process causes an unhandled trap in user mode. // The 'entry' parameter is as in proc_save(). void gcc_noreturn proc_ret(trapframe *tf, int entry) { proc *p; proc *cp; cp = cpu_cur()->proc; p = cp->parent; cprintf("proc_ret child=%p parent=%p\n", cp, p); spinlock_acquire(&(p->lock)); spinlock_acquire(&(cp->lock)); cp->state = PROC_STOP; proc_save(cp, tf, entry); spinlock_release(&(cp->lock)); if(p->state == PROC_WAIT && p->waitchild == cp) { proc_run(p); } spinlock_release(&(p->lock)); proc_sched(); }
static void do_get(trapframe *tf, uint32_t cmd) { proc *p = proc_cur(); assert(p->state == PROC_RUN && p->runcpu == cpu_cur()); //cprintf("GET proc %x eip %x esp %x cmd %x\n", p, tf->eip, tf->esp, cmd); spinlock_acquire(&p->lock); // Find the named child process; DON'T create if it doesn't exist uint32_t cn = tf->regs.edx & 0xff; proc *cp = p->child[cn]; if (!cp) cp = &proc_null; // Synchronize with child if necessary. if (cp->state != PROC_STOP) proc_wait(p, cp, tf); // Since the child is now stopped, it's ours to control; // we no longer need our process lock - // and we don't want to be holding it if usercopy() below aborts. spinlock_release(&p->lock); // Get child's general register state if (cmd & SYS_REGS) { int len = offsetof(procstate, fx); // just integer regs if (cmd & SYS_FPU) len = sizeof(procstate); // whole shebang usercopy(tf, 1, &cp->sv, tf->regs.ebx, len); // Copy child process's trapframe into user space procstate *cs = (procstate*) tf->regs.ebx; memcpy(cs, &cp->sv, len); } uint32_t sva = tf->regs.esi; uint32_t dva = tf->regs.edi; uint32_t size = tf->regs.ecx; switch (cmd & SYS_MEMOP) { case 0: // no memory operation break; case SYS_COPY: case SYS_MERGE: // validate source region if (PTOFF(sva) || PTOFF(size) || sva < VM_USERLO || sva > VM_USERHI || size > VM_USERHI-sva) systrap(tf, T_GPFLT, 0); // fall thru... case SYS_ZERO: // validate destination region if (PTOFF(dva) || PTOFF(size) || dva < VM_USERLO || dva > VM_USERHI || size > VM_USERHI-dva) systrap(tf, T_GPFLT, 0); switch (cmd & SYS_MEMOP) { case SYS_ZERO: // zero memory and clear permissions pmap_remove(p->pdir, dva, size); break; case SYS_COPY: // copy from local src to dest in child pmap_copy(cp->pdir, sva, p->pdir, dva, size); break; case SYS_MERGE: // merge from local src to dest in child pmap_merge(cp->rpdir, cp->pdir, sva, p->pdir, dva, size); break; } break; default: systrap(tf, T_GPFLT, 0); } if (cmd & SYS_PERM) { // validate destination region if (PGOFF(dva) || PGOFF(size) || dva < VM_USERLO || dva > VM_USERHI || size > VM_USERHI-dva) systrap(tf, T_GPFLT, 0); if (!pmap_setperm(p->pdir, dva, size, cmd & SYS_RW)) panic("pmap_get: no memory to set permissions"); } if (cmd & SYS_SNAP) systrap(tf, T_GPFLT, 0); // only valid for PUT trap_return(tf); // syscall completed }
static void do_put(trapframe *tf, uint32_t cmd) { proc *p = proc_cur(); assert(p->state == PROC_RUN && p->runcpu == cpu_cur()); cprintf("PUT proc %x eip %x esp %x cmd %x\n", p, tf->eip, tf->esp, cmd); spinlock_acquire(&p->lock); // Find the named child process; create if it doesn't exist uint32_t cn = tf->regs.edx & 0xff; proc *cp = p->child[cn]; if (!cp) { cp = proc_alloc(p, cn); if (!cp) // XX handle more gracefully panic("sys_put: no memory for child"); } // Synchronize with child if necessary. if (cp->state != PROC_STOP) proc_wait(p, cp, tf); // Since the child is now stopped, it's ours to control; // we no longer need our process lock - // and we don't want to be holding it if usercopy() below aborts. spinlock_release(&p->lock); // Put child's general register state if (cmd & SYS_REGS) { int len = offsetof(procstate, fx); // just integer regs if (cmd & SYS_FPU) len = sizeof(procstate); // whole shebang usercopy(tf,0,&cp->sv, tf->regs.ebx, len); // Copy user's trapframe into child process procstate *cs = (procstate*) tf->regs.ebx; memcpy(&cp->sv, cs, len); // Make sure process uses user-mode segments and eflag settings cp->sv.tf.ds = CPU_GDT_UDATA | 3; cp->sv.tf.es = CPU_GDT_UDATA | 3; cp->sv.tf.cs = CPU_GDT_UCODE | 3; cp->sv.tf.ss = CPU_GDT_UDATA | 3; cp->sv.tf.eflags &= FL_USER; cp->sv.tf.eflags |= FL_IF; // enable interrupts } uint32_t sva = tf->regs.esi; uint32_t dva = tf->regs.edi; uint32_t size = tf->regs.ecx; switch (cmd & SYS_MEMOP) { case 0: // no memory operation break; case SYS_COPY: // validate source region if (PTOFF(sva) || PTOFF(size) || sva < VM_USERLO || sva > VM_USERHI || size > VM_USERHI-sva) systrap(tf, T_GPFLT, 0); // fall thru... case SYS_ZERO: // validate destination region if (PTOFF(dva) || PTOFF(size) || dva < VM_USERLO || dva > VM_USERHI || size > VM_USERHI-dva) systrap(tf, T_GPFLT, 0); switch (cmd & SYS_MEMOP) { case SYS_ZERO: // zero memory and clear permissions pmap_remove(cp->pdir, dva, size); break; case SYS_COPY: // copy from local src to dest in child pmap_copy(p->pdir, sva, cp->pdir, dva, size); break; } break; default: systrap(tf, T_GPFLT, 0); } if (cmd & SYS_PERM) { // validate destination region if (PGOFF(dva) || PGOFF(size) || dva < VM_USERLO || dva > VM_USERHI || size > VM_USERHI-dva) systrap(tf, T_GPFLT, 0); if (!pmap_setperm(cp->pdir, dva, size, cmd & SYS_RW)) panic("pmap_put: no memory to set permissions"); } if (cmd & SYS_SNAP) // Snapshot child's state pmap_copy(cp->pdir, VM_USERLO, cp->rpdir, VM_USERLO, VM_USERHI-VM_USERLO); // Start the child if requested if (cmd & SYS_START) proc_ready(cp); trap_return(tf); // syscall completed }
// Check whether this cpu is holding the lock. int spinlock_holding(spinlock *lock) { return lock->cpu == cpu_cur() && lock->locked; }