int execv(const char *path, char *const argv[]) { // We'll build the new program in child 0, // which never represents a forked child since 0 is an invalid pid. // First clear out the new program's entire address space. sys_put(SYS_ZERO, 0, NULL, NULL, (void*)VM_USERLO, VM_USERHI-VM_USERLO); // Load the ELF executable into child 0. if (exec_readelf(path) < 0) return -1; // Setup child 0's stack with the argument array. intptr_t esp = exec_copyargs(argv); // Copy our Unix file system and process state into the child. sys_put(SYS_COPY, 0, NULL, (void*)VM_FILELO, (void*)VM_FILELO, VM_FILEHI-VM_FILELO); // Copy child 0's entire memory state onto ours // and start the new program. See lib/entry.S for details. exec_start(esp); }
intptr_t exec_copyargs(char *const argv[]) { // Give the process a nice big 4MB, zero-filled stack. sys_get(SYS_ZERO | SYS_PERM | SYS_READ | SYS_WRITE, 0, NULL, NULL, (void*)VM_SCRATCHLO, PTSIZE); #if SOL >= 4 // How many arguments? int argc; for (argc = 0; argv[argc] != NULL; argc++) ; // Make room for the argv array intptr_t esp = VM_STACKHI; intptr_t scratchofs = VM_SCRATCHLO - (VM_STACKHI-PTSIZE); esp -= (argc+1) * sizeof(intptr_t); // room for arguments plus NULL intptr_t dargv = esp; // Copy the argument strings int i; for (i = 0; i < argc; i++) { int len = strlen(argv[i]); esp -= len+1; strcpy((void*)esp + scratchofs, argv[i]); ((intptr_t*)(dargv + scratchofs))[i] = esp; } esp &= ~3; // get esp word-aligned again // Push the arguments to main() esp -= 4; *(intptr_t*)(esp + scratchofs) = dargv; esp -= 4; *(intptr_t*)(esp + scratchofs) = argc; #else // ! SOL >= 4 // Lab 4: insert your code here to copy our command-line arguments // onto the new process's stack, taking into account the fact that // the stack area is mapped at VM_SCRATCHLO to VM_SCRATCHLO+PTSIZE // in _our_ address space while we're copying the arguments, // but the pointers we're writing into this space will be // interpreted by the newly executed process, // where the stack will be mapped from VM_STACKHI-PTSIZE to VM_STACKHI. warn("exec_copyargs not implemented yet"); intptr_t esp = VM_STACKHI; // no arguments - fix this. #endif // ! SOL >= 4 // Copy the stack into its correct position in child 0. sys_put(SYS_COPY, 0, NULL, (void*)VM_SCRATCHLO, (void*)VM_STACKHI-PTSIZE, PTSIZE); return esp; }
void proc_check(void) { // Spawn 2 child processes, executing on statically allocated stacks. cprintf("in proc_check()\n"); int i; for (i = 0; i < 4; i++) { // Setup register state for child uint32_t *esp = (uint32_t*) &child_stack[i][PAGESIZE]; *--esp = i; // push argument to child() function *--esp = 0; // fake return address child_state.tf.eip = (uint32_t) child; child_state.tf.esp = (uint32_t) esp; // Use PUT syscall to create each child, // but only start the first 2 children for now. cprintf("spawning child %d\n", i); sys_put(SYS_REGS | (i < 2 ? SYS_START : 0), i, &child_state, NULL, NULL, 0); } cprintf("proc_check() created childs\n"); // Wait for both children to complete. // This should complete without preemptive scheduling // when we're running on a 2-processor machine. for (i = 0; i < 2; i++) { cprintf("waiting for child %d\n", i); sys_get(SYS_REGS, i, &child_state, NULL, NULL, 0); } cprintf("proc_check() 2-child test succeeded\n"); // (Re)start all four children, and wait for them. // This will require preemptive scheduling to complete // if we have less than 4 CPUs. cprintf("proc_check: spawning 4 children\n"); for (i = 0; i < 4; i++) { cprintf("spawning child %d\n", i); sys_put(SYS_START, i, NULL, NULL, NULL, 0); } // Wait for all 4 children to complete. for (i = 0; i < 4; i++) sys_get(0, i, NULL, NULL, NULL, 0); cprintf("proc_check() 4-child test succeeded\n"); // Now do a trap handling test using all 4 children - // but they'll _think_ they're all child 0! // (We'll lose the register state of the other children.) i = 0; sys_get(SYS_REGS, i, &child_state, NULL, NULL, 0); // get child 0's state assert(recovargs == NULL); do { sys_put(SYS_REGS | SYS_START, i, &child_state, NULL, NULL, 0); sys_get(SYS_REGS, i, &child_state, NULL, NULL, 0); if (recovargs) { // trap recovery needed trap_check_args *args = recovargs; cprintf("recover from trap %d\n", child_state.tf.trapno); child_state.tf.eip = (uint32_t) args->reip; args->trapno = child_state.tf.trapno; } else assert(child_state.tf.trapno == T_SYSCALL); i = (i+1) % 4; // rotate to next child proc } while (child_state.tf.trapno != T_SYSCALL); assert(recovargs == NULL); cprintf("proc_check() trap reflection test succeeded\n"); cprintf("proc_check() succeeded!\n"); }
int exec_readelf(const char *path) { // We'll load the ELF image into a scratch area in our address space. sys_get(SYS_ZERO, 0, NULL, NULL, (void*)VM_SCRATCHLO, EXEMAX); // Open the ELF image to load. filedesc *fd = filedesc_open(NULL, path, O_RDONLY, 0); if (fd == NULL) return -1; void *imgdata = FILEDATA(fd->ino); size_t imgsize = files->fi[fd->ino].size; // Make sure it looks like an ELF image. elfhdr *eh = imgdata; if (imgsize < sizeof(*eh) || eh->e_magic != ELF_MAGIC) { warn("exec_readelf: ELF header not found"); goto err; } // Load each program segment into the scratch area proghdr *ph = imgdata + eh->e_phoff; proghdr *eph = ph + eh->e_phnum; if (imgsize < (void*)eph - imgdata) { warn("exec_readelf: ELF program header truncated"); goto err; } for (; ph < eph; ph++) { if (ph->p_type != ELF_PROG_LOAD) continue; // The executable should fit in the first 4MB of user space. intptr_t valo = ph->p_va; intptr_t vahi = valo + ph->p_memsz; if (valo < VM_USERLO || valo > VM_USERLO+EXEMAX || vahi < valo || vahi > VM_USERLO+EXEMAX) { warn("exec_readelf: executable image too large " "(%d bytes > %d max)", vahi-valo, EXEMAX); goto err; } // Map all pages the segment touches in our scratch region. // They've already been zeroed by the SYS_ZERO above. intptr_t scratchofs = VM_SCRATCHLO - VM_USERLO; intptr_t pagelo = ROUNDDOWN(valo, PAGESIZE); intptr_t pagehi = ROUNDUP(vahi, PAGESIZE); sys_get(SYS_PERM | SYS_READ | SYS_WRITE, 0, NULL, NULL, (void*)pagelo + scratchofs, pagehi - pagelo); // Initialize the file-loaded part of the ELF image. // (We could use copy-on-write if SYS_COPY // supports copying at arbitrary page boundaries.) intptr_t filelo = ph->p_offset; intptr_t filehi = filelo + ph->p_filesz; if (filelo < 0 || filelo > imgsize || filehi < filelo || filehi > imgsize) { warn("exec_readelf: loaded section out of bounds"); goto err; } memcpy((void*)valo + scratchofs, imgdata + filelo, filehi - filelo); // Finally, remove write permissions on read-only segments. if (!(ph->p_flags & ELF_PROG_FLAG_WRITE)) sys_get(SYS_PERM | SYS_READ, 0, NULL, NULL, (void*)pagelo + scratchofs, pagehi - pagelo); } // Copy the ELF image into its correct position in child 0. sys_put(SYS_COPY, 0, NULL, (void*)VM_SCRATCHLO, (void*)VM_USERLO, EXEMAX); // The new program should have the same entrypoint as we do! if (eh->e_entry != (intptr_t)start) { warn("exec_readelf: executable has a different start address"); goto err; } filedesc_close(fd); // Done with the ELF file return 0; err: filedesc_close(fd); return -1; }