voidenv_init(void) { int i; /* Step 1: Initialize 'env_free_list' with 'LIST_INIT' and 'env_sched_list' with * 'TAILQ_INIT'. */ /* Exercise 3.1: Your code here. (1/2) */
/* Step 2: Traverse the elements of 'envs' array, set their status to 'ENV_FREE' and insert * them into the 'env_free_list'. Make sure, after the insertion, the order of envs in the * list should be the same as they are in the 'envs' array. */
/* Exercise 3.1: Your code here. (2/2) */
for (i = NENV - 1; i >= 0; --i) { envs[i].env_status = ENV_FREE; LIST_INSERT_HEAD(&env_free_list, &envs[i], env_link); } ...... }
/* Step 1: Map virtual address space to physical address space. */ for (int i = 0; i < size; i += PAGE_SIZE) { /* * Hint: * Map the virtual page 'va + i' to the physical page 'pa + i' using 'page_insert'. * Use 'pa2page' to get the 'struct Page *' of the physical address. */ /* Exercise 3.2: Your code here. */ panic_on(page_insert(pgdir, asid, pa2page(pa + i), va + i, perm)); } }
staticintenv_setup_vm(struct Env *e) { /* Step 1: * Allocate a page for the page directory with 'page_alloc'. * Increase its 'pp_ref' and assign its kernel address to 'e->env_pgdir'. * * Hint: * You can get the kernel address of a specified physical page using 'page2kva'. */ structPage *p; try(page_alloc(&p)); /* Exercise 3.3: Your code here. */
p->pp_ref++; e->env_pgdir = (Pde *)page2kva(p);
/* Step 2: Copy the template page directory 'base_pgdir' to 'e->env_pgdir'. */ /* Hint: * As a result, the address space of all envs is identical in [UTOP, UVPT). * See include/mmu.h for layout. */ memcpy(e->env_pgdir + PDX(UTOP), base_pgdir + PDX(UTOP), sizeof(Pde) * (PDX(UVPT) - PDX(UTOP)));
/* Step 3: Map its own page table at 'UVPT' with readonly permission. * As a result, user programs can read its page table through 'UVPT' */ e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_V; return0; }
intenv_alloc(struct Env **new, u_int parent_id) { int r; structEnv *e;
/* Step 1: Get a free Env from 'env_free_list' */ /* Exercise 3.4: Your code here. (1/4) */
e = LIST_FIRST(&env_free_list); if (e == NULL) { return -E_NO_FREE_ENV; }
/* Step 2: Call a 'env_setup_vm' to initialize the user address space for this new Env. */ /* Exercise 3.4: Your code here. (2/4) */
if ((r = env_setup_vm(e)) != 0) { return r; }
/* Step 3: Initialize these fields for the new Env with appropriate values: * 'env_user_tlb_mod_entry' (lab4), 'env_runs' (lab6), 'env_id' (lab3), 'env_asid' (lab3), * 'env_parent_id' (lab3) * * Hint: * Use 'asid_alloc' to allocate a free asid. * Use 'mkenvid' to allocate a free envid. */ e->env_user_tlb_mod_entry = 0; // for lab4 e->env_runs = 0; // for lab6 /* Exercise 3.4: Your code here. (3/4) */ if ((r = asid_alloc(&e->env_asid)) != 0) { return r; } e->env_id = mkenvid(e); e->env_parent_id = parent_id;
/* Step 4: Initialize the sp and 'cp0_status' in 'e->env_tf'. * Set the EXL bit to ensure that the processor remains in kernel mode during context * recovery. Additionally, set UM to 1 so that when ERET unsets EXL, the processor * transitions to user mode. */ e->env_tf.cp0_status = STATUS_IM7 | STATUS_IE | STATUS_EXL | STATUS_UM; // Reserve space for 'argc' and 'argv'. e->env_tf.regs[29] = USTACKTOP - sizeof(int) - sizeof(char **);
/* Step 5: Remove the new Env from env_free_list. */ /* Exercise 3.4: Your code here. (4/4) */
/* Step 1: Allocate a page with 'page_alloc'. */ /* Exercise 3.5: Your code here. (1/2) */
if ((r = page_alloc(&p)) != 0) { return r; }
/* Step 2: If 'src' is not NULL, copy the 'len' bytes started at 'src' into 'offset' at this * page. */ // Hint: You may want to use 'memcpy'. if (src != NULL) { /* Exercise 3.5: Your code here. (2/2) */
memcpy((void *)page2kva(p) + offset, src, len); }
/* Step 3: Insert 'p' into 'env->env_pgdir' at 'va' with 'perm'. */ return page_insert(env->env_pgdir, env->env_asid, p, va, perm); }
staticvoidload_icode(struct Env *e, constvoid *binary, size_t size) { 315/* Step 1: Use 'elf_from' to parse an ELF header from 'binary'. */ 316const Elf32_Ehdr *ehdr = elf_from(binary, size); 317if (!ehdr) { 318 panic("bad elf at %x", binary); 319 } 320 321/* Step 2: Load the segments using 'ELF_FOREACH_PHDR_OFF' and 'elf_load_seg'. 322 * As a loader, we just care about loadable segments, so parse only program headers here. 323 */ 324size_t ph_off; 325 ELF_FOREACH_PHDR_OFF (ph_off, ehdr) { 326 Elf32_Phdr *ph = (Elf32_Phdr *)(binary + ph_off); 327if (ph->p_type == PT_LOAD) { 328// 'elf_load_seg' is defined in lib/elfloader.c 329// 'load_icode_mapper' defines the way in which a page in this segment 330// should be mapped. 331 panic_on(elf_load_seg(ph, binary + ph->p_offset, load_icode_mapper, e)); 332 } 333 } 334 335/* Step 3: Set 'e->env_tf.cp0_epc' to 'ehdr->e_entry'. */ 336/* Exercise 3.6: Your code here. */ 337 e->env_tf.cp0_epc=ehdr->e_entry; 338 }
T7
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
struct Env *env_create(constvoid *binary, size_t size, int priority) { 349structEnv *e; 350/* Step 1: Use 'env_alloc' to alloc a new env, with 0 as 'parent_id'. */ 351/* Exercise 3.7: Your code here. (1/3) */ 352 panic_on(env_alloc(&e,0)); 353/* Step 2: Assign the 'priority' to 'e' and mark its 'env_status' as runnable. */ 354/* Exercise 3.7: Your code here. (2/3) */ 355 e->env_pri=priority; 356 e->envstatus=ENV_RUNNABLE; 357/* Step 3: Use 'load_icode' to load the image from 'binary', and insert 'e' into 358 * 'env_sched_list' using 'TAILQ_INSERT_HEAD'. */ 359/* Exercise 3.7: Your code here. (3/3) */ 360 load_icode(e,binary,size); 361 TAILQ_INSERT_HEAD(&env_sched_list,e,env_sched_link); 362return e; 363 }
voidenv_run(struct Env *e) { assert(e->env_status == ENV_RUNNABLE); // WARNING BEGIN: DO NOT MODIFY FOLLOWING LINES! #ifdef MOS_PRE_ENV_RUN MOS_PRE_ENV_RUN_STMT #endif // WARNING END
/* Step 1: * If 'curenv' is NULL, this is the first time through. * If not, we may be switching from a previous env, so save its context into * 'curenv->env_tf' first. */ if (curenv) { curenv->env_tf = *((struct Trapframe *)KSTACKTOP - 1); }
/* Step 3: Change 'cur_pgdir' to 'curenv->env_pgdir', switching to its address space. */ /* Exercise 3.8: Your code here. (1/2) */
cur_pgdir = curenv->env_pgdir;
/* Step 4: Use 'env_pop_tf' to restore the curenv's saved context (registers) and return/go * to user mode. * * Hint: * - You should use 'curenv->env_asid' here. * - 'env_pop_tf' is a 'noreturn' function: it restores PC from 'cp0_epc' thus not * returning to the kernel caller, making 'env_run' a 'noreturn' function as well. */ /* Exercise 3.8: Your code here. (2/2) */ env_pop_tf(&curenv->env_tf, curenv->env_asid); }
T9
注意插入位置为下方
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
.section .text.exc_gen_entry exc_gen_entry: SAVE_ALL /* * Note: When EXL is set or UM is unset, the processor is in kernel mode. * When EXL is set, the value of EPC is not updated when a new exception occurs. * To keep the processor in kernel mode and enable exception reentrancy, * we unset UM and EXL, and unset IE to globally disable interrupts. */ mfc0 t0, CP0_STATUS and t0, t0, ~(STATUS_UM | STATUS_EXL | STATUS_IE) mtc0 t0, CP0_STATUS /* Exercise 3.9: Your code here. */ mfc0 t0, CP0_CAUSE andi t0, 0x7c lw t0, exception_handlers(t0) jr t0
.macro RESET_KCLOCK li t0, TIMER_INTERVAL /* * Hint: * Use 'mtc0' to write an appropriate value into the CP0_COUNT and CP0_COMPARE registers. * Writing to the CP0_COMPARE register will clear the timer interrupt. * The CP0_COUNT register increments at a fixed frequency. When the values of CP0_COUNT and * CP0_COMPARE registers are equal, the timer interrupt will be triggered. * */ /* Exercise 3.11: Your code here. */ mtc0 zero, CP0_COUNT mtc0 t0, CP0_COMPARE .endm
voidschedule(int yield) { staticint count = 0; // remaining time slices of current env structEnv *e = curenv;
/* We always decrease the 'count' by 1. * * If 'yield' is set, or 'count' has been decreased to 0, or 'e' (previous 'curenv') is * 'NULL', or 'e' is not runnable, then we pick up a new env from 'env_sched_list' (list of * all runnable envs), set 'count' to its priority, and schedule it with 'env_run'. **Panic * if that list is empty**. * * (Note that if 'e' is still a runnable env, we should move it to the tail of * 'env_sched_list' before picking up another env from its head, or we will schedule the * head env repeatedly.) * * Otherwise, we simply schedule 'e' again. * * You may want to use macros below: * 'TAILQ_FIRST', 'TAILQ_REMOVE', 'TAILQ_INSERT_TAIL' */ /* Exercise 3.12: Your code here. */ if (yield || count == 0 || e == NULL || e->env_status != ENV_RUNNABLE) { if (e != NULL && e->env_status == ENV_RUNNABLE) { TAILQ_REMOVE(&env_sched_list, e, env_sched_link); TAILQ_INSERT_TAIL(&env_sched_list, e, env_sched_link); } e = TAILQ_FIRST(&env_sched_list); if (e == NULL) { panic("schedule: no runnable envs\n"); } count = e->env_pri; } count--; env_run(e); }