// Spawn a child process from a program image loaded from the file system. // prog: the pathname of the program to run. // argv: pointer to null-terminated array of pointers to strings, // which will be passed to the child as its command-line arguments. // Returns child envid on success, < 0 on failure. int spawn(const char *prog, const char **argv) { unsigned char elf_buf[512]; struct Trapframe child_tf; envid_t child; // Insert your code, following approximately this procedure: // // - Open the program file. // // - Read the ELF header, as you have before, and sanity check its // magic number. (Check out your load_icode!) // // - Use sys_exofork() to create a new environment. // // - Set child_tf to an initial struct Trapframe for the child. // Hint: The sys_exofork() system call has already created // a good basis, in envs[ENVX(child)].env_tf. // Hint: You must do something with the program's entry point. // What? (See load_icode!) // // - Call the init_stack() function above to set up // the initial stack page for the child environment. // // - Map all of the program's segments that are of p_type // ELF_PROG_LOAD into the new environment's address space. // Use the p_flags field in the Proghdr for each segment // to determine how to map the segment: // // * If the ELF flags do not include ELF_PROG_FLAG_WRITE, // then the segment contains text and read-only data. // Use read_map() to read the contents of this segment, // and map the pages it returns directly into the child // so that multiple instances of the same program // will share the same copy of the program text. // Be sure to map the program text read-only in the child. // Read_map is like read but returns a pointer to the data in // *blk rather than copying the data into another buffer. // // * If the ELF segment flags DO include ELF_PROG_FLAG_WRITE, // then the segment contains read/write data and bss. // As with load_icode() in Lab 3, such an ELF segment // occupies p_memsz bytes in memory, but only the FIRST // p_filesz bytes of the segment are actually loaded // from the executable file - you must clear the rest to zero. // For each page to be mapped for a read/write segment, // allocate a page in the parent temporarily at UTEMP, // read() the appropriate portion of the file into that page // and/or use memset() to zero non-loaded portions. // (You can avoid calling memset(), if you like, if // page_alloc() returns zeroed pages already.) // Then insert the page mapping into the child. // Look at init_stack() for inspiration. // Be sure you understand why you can't use read_map() here. // // Note: None of the segment addresses or lengths above // are guaranteed to be page-aligned, so you must deal with // these non-page-aligned values appropriately. // The ELF linker does, however, guarantee that no two segments // will overlap on the same page; and it guarantees that // PGOFF(ph->p_offset) == PGOFF(ph->p_va). // // - Call sys_env_set_trapframe(child, &child_tf) to set up the // correct initial eip and esp values in the child. // // - Start the child process running with sys_env_set_status(). // LAB 5: Your code here. (void) child; //panic("spawn unimplemented!"); int r; int i; int fdnum; void *blk; uintptr_t init_esp; uint32_t readsize; uintptr_t va; uint32_t pdex; uint32_t ptex; uint32_t pn; struct Elf *elf; struct Proghdr *ph, *eph; if ((fdnum=open(prog, O_RDONLY)) < 0) return fdnum; if ((r=read(fdnum, (void *)elf_buf, 512)) < 0) return r; elf = (struct Elf*) elf_buf; if (elf->e_magic != ELF_MAGIC) return -E_NOT_EXEC; if ((child=sys_exofork()) < 0) return child; // return point of child if (child == 0) { env = &envs[ENVX(sys_getenvid())]; return 0; } child_tf = envs[ENVX(child)].env_tf; child_tf.tf_eip = elf->e_entry; if ((r=init_stack(child, argv, &init_esp)) < 0) return r; child_tf.tf_esp = init_esp; ph = (struct Proghdr *) (elf_buf+elf->e_phoff); eph = ph + elf->e_phnum; for (; ph != eph; ph++) { if (ph->p_type == ELF_PROG_LOAD) { // sigh, remember to carefully read the instr above!!!!!!!! // make ph->p_va && p_offset page-aligned uint32_t aligned_va = ROUNDDOWN(ph->p_va, PGSIZE); uint32_t aligned_offset = ROUNDDOWN(ph->p_offset, PGSIZE); if (ph->p_flags & ELF_PROG_FLAG_WRITE) { for (i = 0; i < ph->p_memsz; i += PGSIZE) { if ((r=sys_page_alloc(0, (void *) UTEMP, PTE_U|PTE_P|PTE_W)) < 0) return r; if (i < ph->p_filesz) { /*seek(fdnum, ph->p_offset+i);*/ seek(fdnum,aligned_offset+i); readsize = (ph->p_filesz-i >= PGSIZE) ? PGSIZE:(ph->p_filesz-i); read(fdnum,(void *) UTEMP, readsize); // if the segment can full-fill the page, set tail section to 0x0 if (readsize < PGSIZE) memset((void *) (UTEMP+readsize), 0x0, PGSIZE-readsize); } else { memset((void *) UTEMP, 0x0, PGSIZE); } sys_page_map(0, UTEMP, child, (void *) (aligned_va+i), PTE_U|PTE_W|PTE_P); sys_page_unmap(0, UTEMP); } } else { for (i = 0; i < ph->p_memsz; i+=PGSIZE) { if ((r=read_map(fdnum, aligned_offset+i, &blk)) < 0) return r; if ((r=sys_page_map(0, blk, child, (void *) (aligned_va+i), PTE_U|PTE_P)) < 0) return r; } } } } close(fdnum); if ((r=sys_env_set_trapframe(child, &child_tf)) < 0) return r; if ((r=sys_env_set_status(child, ENV_RUNNABLE)) < 0) return r; for (pdex = PDX(0); pdex < PDX(UTOP); pdex++) { if (vpd[pdex] & PTE_P) { for ( ptex = 0; ptex < NPTENTRIES; ptex++) { pn = (pdex << 10) + ptex; if ((vpt[pn] & PTE_P) && (vpt[pn] & PTE_SHARE)) { va = (uintptr_t) PGADDR(pdex, ptex, 0); if ((r=sys_page_map(0, (void *) va, child, (void *) va, vpt[pn] & PTE_USER)) < 0) return r; } } } } return child; }
// Challenge! int sfork(void) { //panic("sfork not implemented"); envid_t envid=-1; pte_t ptentry=0, ptable=0, page_num=0; pde_t pdentry=0; uintptr_t addr=0; int pdindex = 0, pte_perm=0, pte_loop=0; int r=-1; set_pgfault_handler(pgfault); if((envid=sys_exofork())<0) panic("\nCannot create a child process:%e\n",envid); //cprintf("\nenvid of newly created child:%x\n",envid); if (envid == 0) { // We're the child. // The copied value of the global variable 'thisenv' // is no longer valid (it refers to the parent!). // Fix it and return 0. thisenv = &envs[ENVX(sys_getenvid())]; set_pgfault_handler(pgfault); return 0; } //Incrementing by PGSIZE in loop because 1 page of pgsize corresponds to 1 page taable entry //Incrementing the address by 4MB because uvpd has no entry means 1 uvpd->4 MB region so need to skip it. while(addr<(USTACKTOP-PGSIZE)) { //cprintf("parent address:%x",addr); if(uvpd[PDX(addr)] & PTE_P) { if(uvpt[PGNUM(addr)] & PTE_P) { //cprintf("\ncalling duppgae for address %x\n",addr); s_duppage(envid, PGNUM(addr)); } addr += PGSIZE; } else { addr = addr + PTSIZE; } } sf_stack_duppage(envid, (void *)USTACKTOP-PGSIZE); /*addr = USTACKTOP-2*PGSIZE; if(uvpd[PDX(addr)] & PTE_P) { if(uvpt[PGNUM(addr)] & PTE_P) { //cprintf("\ncalling duppgae for address %x\n",addr); s_duppage(envid, PGNUM(addr)); } addr += PGSIZE; }*/ if ((r = sys_page_alloc(envid,(void *)UXSTACKTOP-PGSIZE, PTE_P|PTE_U|PTE_W)) < 0) panic("sys_page_alloc: %e", r); if ((r = sys_env_set_pgfault_upcall(envid, _pgfault_upcall)) < 0) panic("pagefault upcall setup error: %e", r); sys_env_set_status(envid, ENV_RUNNABLE); return envid; }
// // User-level fork with copy-on-write. // Set up our page fault handler appropriately. // Create a child. // Copy our address space and page fault handler setup to the child. // Then mark the child as runnable and return. // // Returns: child's envid to the parent, 0 to the child, < 0 on error. // It is also OK to panic on error. // // Hint: // Use uvpd, uvpt, and duppage. // Remember to fix "thisenv" in the child process. // Neither user exception stack should ever be marked copy-on-write, // so you must allocate a new page for the child's user exception stack. // envid_t fork(void) { envid_t child; unsigned pagenum; int i; // First set up the page fault handler set_pgfault_handler(pgfault); // Create a new child environment and store its id. Return the error // on failure child = sys_exofork(); if(child < 0) { // There was some error, return the error return child; } else if(child == 0) { // This is the child, thisenv needs to be fixed thisenv = &envs[ENVX(sys_getenvid())]; return child; } // Map every page under UTOP using duppage. Writable pages will // be mapped to a COW page, and non-writable pages will be mapped // to read-only pages. Very simple. // // The only exception is the exception stack (located at UXSTACKTOP- // PGSIZE). A new page should be allocated in the child for this. for(pagenum = 0; pagenum < PGNUM(UTOP); pagenum++) { // Check to see if the page directory entry and page table // entry for this page exist. if((uvpd[PDX(pagenum*PGSIZE)]&PTE_P) == 0 || (uvpt[pagenum]&PTE_P) == 0) continue; if(pagenum == PGNUM(UXSTACKTOP-PGSIZE)) // Found the exception stack page sys_page_alloc(child, (void *)(pagenum*PGSIZE), PTE_U|PTE_W); else // Duplicate the page duppage(child, pagenum); } // Get the child environment struct and set its pagefault handlers // by copying the ones from the parent. if(sys_env_set_pgfault_upcall(child, thisenv->env_pgfault_upcall) != 0) panic("unable to set child page fault upcall"); if(sys_env_set_global_pgfault(child, thisenv->env_pgfault_global) != 0) panic("unable to set child page fault handler"); for(i = 0; i < MAXHANDLERS; i++) { // Don't add a handler that doesn't exist if(thisenv->env_pgfault_handlers[i].erh_maxaddr == 0) continue; // Attempt to set the handler if(sys_env_set_region_pgfault(child, thisenv->env_pgfault_handlers[i].erh_handler, (void *)thisenv->env_pgfault_handlers[i].erh_minaddr, (void *)thisenv->env_pgfault_handlers[i].erh_maxaddr) != 0); panic("unable to set child page fault handler"); } // Finally, mark the child as runnable. if(sys_env_set_status(child, ENV_RUNNABLE) != 0) panic("can't set child status to runnable"); // Return the child id return child; }
// Choose a user environment to run and run it. void sched_yield(void) { // Implement simple round-robin scheduling. // Search through 'envs' for a runnable environment, // in circular fashion starting after the previously running env, // and switch to the first such environment found. // It's OK to choose the previously running env if no other env // is runnable. // But never choose envs[0], the idle environment, // unless NOTHING else is runnable. // LAB 4: Your code here. #if LAB4A_SCHED_PRI int cur_index=0, next_index=0, priority_index=0,selected_index=0; int priority_selected = 0 ; if(NULL != curenv){ cur_index = ENVX(curenv->env_id); } next_index = (cur_index + 1)%NENV; for(;next_index != cur_index; next_index=(next_index+1)%NENV){ if((envs[next_index].env_status == ENV_RUNNABLE) && (next_index != 0)){ if(next_index & 0x01){ priority_index = next_index; priority_selected = 1; }else{ if(selected_index == 0) { selected_index=next_index; } } // env_run(&envs[next_index]); } } if(priority_selected){ env_run(&envs[priority_index]); }else{ env_run(&envs[selected_index]); } #else struct Env *penv, *pstart; int i = 0; if ((curenv == NULL) || (curenv == &envs[NENV - 1])) { // Skip envs[0] pstart = &envs[1]; } else { pstart = curenv + 1; } penv = pstart; while (1) { ++i; if ((penv == pstart) && (i > 1)) { // We've come back to start full-circle, time to break out break; } if (penv->env_status == ENV_RUNNABLE) { env_run(penv); break; } if (penv == &envs[NENV - 1]) { // Skip envs[0] penv = &envs[1]; } else { ++penv; } } #endif // Run the special idle environment when nothing else is runnable. if (envs[0].env_status == ENV_RUNNABLE) env_run(&envs[0]); else { cprintf("Destroyed all environments - nothing more to do!\n"); while (1) monitor(NULL); } }
// Spawn a child process from a program image loaded from the file system. // prog: the pathname of the program to run. // argv: pointer to null-terminated array of pointers to strings, // which will be passed to the child as its command-line arguments. // Returns child envid on success, < 0 on failure. int spawn(const char *prog, const char **argv) { unsigned char elf_buf[512]; struct Trapframe child_tf; envid_t child; struct Env * child_env; int fd, i, r; struct Elf *elf; struct Proghdr *ph; int perm; // This code follows this procedure: // // - Open the program file. // // - Read the ELF header, as you have before, and sanity check its // magic number. (Check out your load_icode!) // // - Use sys_exofork() to create a new environment. // // - Set child_tf to an initial struct Trapframe for the child. // // - Call the init_stack() function above to set up // the initial stack page for the child environment. // // - Map all of the program's segments that are of p_type // ELF_PROG_LOAD into the new environment's address space. // Use the p_flags field in the Proghdr for each segment // to determine how to map the segment: // // * If the ELF flags do not include ELF_PROG_FLAG_WRITE, // then the segment contains text and read-only data. // Use read_map() to read the contents of this segment, // and map the pages it returns directly into the child // so that multiple instances of the same program // will share the same copy of the program text. // Be sure to map the program text read-only in the child. // Read_map is like read but returns a pointer to the data in // *blk rather than copying the data into another buffer. // // * If the ELF segment flags DO include ELF_PROG_FLAG_WRITE, // then the segment contains read/write data and bss. // As with load_icode() in Lab 3, such an ELF segment // occupies p_memsz bytes in memory, but only the FIRST // p_filesz bytes of the segment are actually loaded // from the executable file - you must clear the rest to zero. // For each page to be mapped for a read/write segment, // allocate a page in the parent temporarily at UTEMP, // read() the appropriate portion of the file into that page // and/or use memset() to zero non-loaded portions. // (You can avoid calling memset(), if you like, if // page_alloc() returns zeroed pages already.) // Then insert the page mapping into the child. // Look at init_stack() for inspiration. // Be sure you understand why you can't use read_map() here. // // Note: None of the segment addresses or lengths above // are guaranteed to be page-aligned, so you must deal with // these non-page-aligned values appropriately. // The ELF linker does, however, guarantee that no two segments // will overlap on the same page; and it guarantees that // PGOFF(ph->p_offset) == PGOFF(ph->p_va). // // - Call sys_env_set_trapframe(child, &child_tf) to set up the // correct initial eip and esp values in the child. // // - Start the child process running with sys_env_set_status(). if ((r = open(prog, O_RDONLY)) < 0) return r; fd = r; // Read elf header elf = (struct Elf*) elf_buf; if (readn(fd, elf_buf, sizeof(elf_buf)) != sizeof(elf_buf) || elf->e_magic != ELF_MAGIC) { close(fd); cprintf("elf magic %08x want %08x\n", elf->e_magic, ELF_MAGIC); return -E_NOT_EXEC; } // Create new child environment if ((r = sys_exofork()) < 0) return r; child = r; // Set up trap frame, including initial stack. child_tf = envs[ENVX(child)].env_tf; child_tf.tf_eip = elf->e_entry; if ((r = init_stack(child, argv, &(child_tf.tf_esp))) < 0) return r; // Set up program segments as defined in ELF header. ph = (struct Proghdr*) (elf_buf + elf->e_phoff); for (i = 0; i < elf->e_phnum; i++, ph++) { if (ph->p_type != ELF_PROG_LOAD) continue; perm = PTE_P | PTE_U; if (ph->p_flags & ELF_PROG_FLAG_WRITE) perm |= PTE_W; if ((r = map_segment(child, ph->p_va, ph->p_memsz, fd, ph->p_filesz, ph->p_offset, perm)) < 0) goto error; } close(fd); fd = -1; cprintf("copy sharing pages\n"); // Copy shared library state. if ((r = copy_shared_pages(child)) < 0) panic("copy_shared_pages: %e", r); cprintf("complete copy sharing pages\n"); if ((r = sys_env_set_trapframe(child, &child_tf)) < 0) panic("sys_env_set_trapframe: %e", r); //thisenv = &envs[ENVX(child)]; //cprintf("%s %x\n",__func__,thisenv->env_id); if ((r = sys_env_set_status(child, ENV_RUNNABLE)) < 0) panic("sys_env_set_status: %e", r); return child; error: sys_env_destroy(child); close(fd); return r; }