PS: 這個實驗中使用術語 environment 而不是傳統的 processlinux
這個lab須要你瞭解 GCC inline assembly language:ide
JOS的Env結構體與xv6中的proc結構體類似函數
修改kern/pmap.c
中的mem_init()
:測試
// Make 'envs' point to an array of size 'NENV' of 'struct Env'. // LAB 3: Your code here. envs = (struct Env*)boot_alloc(NENV * sizeof(struct Env)); memset(envs, 0, NENV * sizeof(struct Env)); // 省略... // Map the 'envs' array read-only by the user at linear address UENVS // LAB 3: Your code here. boot_map_region(kern_pgdir, UENVS, NENV * sizeof(struct Env), PADDR(envs), PTE_U);
運行,會出現check_kern_pgdir() succeeded!
ui
文件env.c
中的env_init()
:this
void env_init(void) { // Set up envs array // LAB 3: Your code here. env_free_list = NULL; for (int i = NENV - 1; i >= 0; i--) { envs[i].env_id = 0; envs[i].env_link = env_free_list; env_free_list = &envs[i]; } // Per-CPU part of the initialization env_init_percpu(); }
env_setup_vm()
:spa
static int env_setup_vm(struct Env *e) { int i; struct PageInfo *p = NULL; // Allocate a page for the page directory if (!(p = page_alloc(ALLOC_ZERO))) return -E_NO_MEM; // LAB 3: Your code here. e->env_pgdir = (pde_t*)page2kva(p); memcpy(e->env_pgdir, kern_pgdir, PGSIZE); p->pp_ref++; // UVPT maps the env's own page table read-only. // Permissions: kernel R, user R e->env_pgdir[PDX(UVPT)] = PADDR(e->env_pgdir) | PTE_P | PTE_U; return 0; }
region_alloc()
:debug
static void region_alloc(struct Env *e, void *va, size_t len) { struct PageInfo* p; uintptr_t end = ROUNDUP((uintptr_t)va + len, PGSIZE); uintptr_t start = ROUNDDOWN((uintptr_t)va, PGSIZE); size_t pgnum = (end - start) >> PGSHIFT; for (size_t i = 0; i < pgnum; i++) { if ((p = page_alloc(0)) == NULL) { panic("region_alloc: %e", -E_NO_MEM); } int r = page_insert(e->env_pgdir, p, (void*)(start + i * PGSIZE), PTE_W | PTE_U); if (r < 0) { panic("region_alloc: %e", r); } } }
load_icode()
:code
static void load_icode(struct Env *e, uint8_t *binary) { // LAB 3: Your code here. struct Elf* elf = (struct Elf*)binary; struct Proghdr *ph, *eph; if (elf->e_magic != ELF_MAGIC) { panic("load_icode: not an ELF file"); } ph = (struct Proghdr*)(binary + elf->e_phoff); eph = ph + elf->e_phnum; lcr3(PADDR(e->env_pgdir)); for (; ph < eph; ph++) { if (ph->p_type == ELF_PROG_LOAD) { if (ph->p_filesz > ph->p_memsz) { panic("load_icode: p_filesz > p_memsz"); } region_alloc(e, (void*)(ph->p_va), ph->p_memsz); memcpy((void*)(ph->p_va), (void*)(binary + ph->p_offset), ph->p_filesz); memset((void*)(ph->p_va + ph->p_filesz), 0, ph->p_memsz - ph->p_filesz); } } e->env_tf.tf_eip = elf->e_entry; // LAB 3: Your code here. region_alloc(e, (void*)(USTACKTOP - PGSIZE), PGSIZE); lcr3(PADDR(kern_pgdir)); }
env_create()
:
void env_create(uint8_t *binary, enum EnvType type) { // LAB 3: Your code here. struct Env* e; int r = env_alloc(&e, 0); if (r < 0) { panic("env_create: %e", r); } e->env_type = type; load_icode(e, binary); }
env_run()
:
void env_run(struct Env *e) { // LAB 3: Your code here. if (curenv && curenv->env_status == ENV_RUNNING) { curenv->env_status = ENV_RUNNABLE; } curenv = e; e->env_status = ENV_RUNNING; e->env_runs++; lcr3(PADDR(e->env_pgdir)); env_pop_tf(&e->env_tf); panic("env_run not yet implemented"); }
若是是用 6.828 補丁版的 QEMU,運行後會看到"Triple fault"的報錯信息,若是不是 6.828 的,會看到CPU重置,系統重啓。
kern/trapentry.S
/* * Lab 3: Your code here for generating entry points for the different traps. */ TRAPHANDLER_NOEC(vector0, T_DIVIDE) TRAPHANDLER_NOEC(vector1, T_DEBUG) TRAPHANDLER_NOEC(vector2, T_NMI) TRAPHANDLER_NOEC(vector3, T_BRKPT) TRAPHANDLER_NOEC(vector4, T_OFLOW) TRAPHANDLER_NOEC(vector5, T_BOUND) TRAPHANDLER_NOEC(vector6, T_ILLOP) TRAPHANDLER_NOEC(vector7, T_DEVICE) TRAPHANDLER(vector8, T_DBLFLT) TRAPHANDLER(vector10, T_TSS) TRAPHANDLER(vector11, T_SEGNP) TRAPHANDLER(vector12, T_STACK) TRAPHANDLER(vector13, T_GPFLT) TRAPHANDLER(vector14, T_PGFLT) TRAPHANDLER_NOEC(vector16, T_FPERR) TRAPHANDLER(vector17, T_ALIGN) TRAPHANDLER_NOEC(vector18, T_MCHK) TRAPHANDLER_NOEC(vector19, T_SIMDERR) TRAPHANDLER_NOEC(vector48, T_SYSCALL) /* * Lab 3: Your code here for _alltraps */ .global _alltraps _alltraps: pushl %ds pushl %es pushal movw $GD_KD, %ax movw %ax, %ds movw %ax, %es pushl %esp call trap
kern/trap.c
void trap_init(void) { extern struct Segdesc gdt[]; // LAB 3: Your code here. void vector0(); void vector1(); void vector2(); void vector3(); void vector4(); void vector5(); void vector6(); void vector7(); void vector8(); void vector10(); void vector11(); void vector12(); void vector13(); void vector14(); void vector16(); void vector17(); void vector18(); void vector19(); void vector48(); SETGATE(idt[T_DIVIDE], 0, GD_KT, vector0, 0) SETGATE(idt[T_DEBUG], 0, GD_KT, vector1, 0) SETGATE(idt[T_NMI], 0, GD_KT, vector2, 0) SETGATE(idt[T_BRKPT], 0, GD_KT, vector3, 3) SETGATE(idt[T_OFLOW], 0, GD_KT, vector4, 0) SETGATE(idt[T_BOUND], 0, GD_KT, vector5, 0) SETGATE(idt[T_ILLOP], 0, GD_KT, vector6, 0) SETGATE(idt[T_DEVICE], 0, GD_KT, vector7, 0) SETGATE(idt[T_DBLFLT], 0, GD_KT, vector8, 0) SETGATE(idt[T_TSS], 0, GD_KT, vector10, 0) SETGATE(idt[T_SEGNP], 0, GD_KT, vector11, 0) SETGATE(idt[T_STACK], 0, GD_KT, vector12, 0) SETGATE(idt[T_GPFLT], 0, GD_KT, vector13, 0) SETGATE(idt[T_PGFLT], 0, GD_KT, vector14, 0) SETGATE(idt[T_FPERR], 0, GD_KT, vector16, 0) SETGATE(idt[T_ALIGN], 0, GD_KT, vector17, 0) SETGATE(idt[T_MCHK], 0, GD_KT, vector18, 0) SETGATE(idt[T_SIMDERR], 0, GD_KT, vector19, 0) // 若是是trap則不會清除IF位,容許cpu響應可屏蔽的硬件中斷 // dpl爲3,則能夠在用戶態進行該中斷 SETGATE(idt[T_SYSCALL], 0, GD_KT, vector48, 3) // Per-CPU setup trap_init_percpu(); }
trap.c
文件:
static void trap_dispatch(struct Trapframe *tf) { // Handle processor exceptions. // LAB 3: Your code here. switch (tf->tf_trapno) { case T_BRKPT: monitor(tf); return; case T_PGFLT: page_fault_handler(tf); return; default: break; } // Unexpected trap: The user process or the kernel has a bug. print_trapframe(tf); if (tf->tf_cs == GD_KT) panic("unhandled trap in kernel"); else { env_destroy(curenv); return; } }
測試breakpoint失敗了,查看文件jos.out.breakpoint
,發現trap 0x0000000d General Protection
,這是由於咱們在用戶模式進行int 3
進入更高特權等級的內核,要求CPL<=DPL,因此DPL應該設置爲3,將trap_init
函數裏的改爲SETGATE(idt[T_BRKPT], 1, GD_KT, vector3, 3)
便可。
static void trap_dispatch(struct Trapframe *tf) { // Handle processor exceptions. // LAB 3: Your code here. switch (tf->tf_trapno) { case T_BRKPT: monitor(tf); return; case T_PGFLT: page_fault_handler(tf); return; case T_SYSCALL: tf->tf_regs.reg_eax = syscall(tf->tf_regs.reg_eax, tf->tf_regs.reg_edx, tf->tf_regs.reg_ecx, tf->tf_regs.reg_ebx, tf->tf_regs.reg_edi, tf->tf_regs.reg_esi); return; default: break; } // Unexpected trap: The user process or the kernel has a bug. print_trapframe(tf); if (tf->tf_cs == GD_KT) panic("unhandled trap in kernel"); else { env_destroy(curenv); return; } }
int32_t syscall(uint32_t syscallno, uint32_t a1, uint32_t a2, uint32_t a3, uint32_t a4, uint32_t a5) { switch (syscallno) { case SYS_cputs: sys_cputs((const char*)a1, a2); return 0; case SYS_cgetc: return sys_cgetc(); case SYS_getenvid: return sys_getenvid(); case SYS_env_destroy: return sys_env_destroy(a1); default: return -E_INVAL; } }
void libmain(int argc, char **argv) { // set thisenv to point at our Env structure in envs[]. // LAB 3: Your code here. thisenv = 0; envid_t env_id = sys_getenvid(); for (int i = 0; i < NENV; i++) { if (envs[i].env_id == env_id) { thisenv = &envs[i]; } }
void page_fault_handler(struct Trapframe *tf) { uint32_t fault_va; // Read processor's CR2 register to find the faulting address fault_va = rcr2(); // Handle kernel-mode page faults. // LAB 3: Your code here. // 若是是在kernel中page fault if ((tf->tf_cs & 0x3) == 0) { panic("page fault in kernel mode!"); }
int user_mem_check(struct Env *env, const void *va, size_t len, int perm) { // LAB 3: Your code here. uintptr_t start = ROUNDDOWN((uint32_t)va, PGSIZE); uintptr_t end = ROUNDDOWN((uint32_t)va + len - 1, PGSIZE); pte_t* pte; perm |= PTE_P; for (uintptr_t cur = start;; cur += PGSIZE) { pte = pgdir_walk(env->env_pgdir, (void*)cur, 0); if (pte == NULL || (*pte & perm) != perm || cur >= ULIM) { user_mem_check_addr = cur == start ? (uintptr_t)va : cur; return -E_FAULT; } if (cur == end) return 0; } return 0; }
static void sys_cputs(const char *s, size_t len) { // Check that the user has permission to read memory [s, s+len). // Destroy the environment if not. // LAB 3: Your code here. user_mem_assert(curenv, s, len, PTE_U); // Print the string supplied by the user. cprintf("%.*s", len, s); }
int debuginfo_eip(uintptr_t addr, struct Eipdebuginfo *info) { // 省略 // Make sure this memory is valid. // Return -1 if it is not. Hint: Call user_mem_check. // LAB 3: Your code here. if (user_mem_check(curenv, (void*)usd, sizeof(struct UserStabData), PTE_U) < 0) { return -1; } // 省略 // Make sure the STABS and string table memory is valid. // LAB 3: Your code here. if (user_mem_check(curenv, (void*)stabs, stab_end - stabs, PTE_U) < 0) { return -1; } if (user_mem_check(curenv, (void*)stabstr, stabstr_end - stabstr, PTE_U) < 0) { return -1; }