/** * Check page table */ void check_pgdir(void) { assert(npage <= KMEMSIZE / PGSIZE); assert(boot_pgdir != NULL && (uint32_t) PGOFF(boot_pgdir) == 0); assert(get_page(boot_pgdir, TEST_PAGE, NULL) == NULL); struct Page *p1, *p2; p1 = alloc_page(); assert(page_insert(boot_pgdir, p1, TEST_PAGE, 0) == 0); pte_t *ptep, perm; assert((ptep = get_pte(boot_pgdir, TEST_PAGE, 0)) != NULL); assert(pa2page(*ptep) == p1); assert(page_ref(p1) == 1); ptep = &((pte_t *) KADDR(PTE_ADDR(boot_pgdir[PDX(TEST_PAGE)])))[1]; assert(get_pte(boot_pgdir, TEST_PAGE + PGSIZE, 0) == ptep); p2 = alloc_page(); ptep_unmap(&perm); ptep_set_u_read(&perm); ptep_set_u_write(&perm); assert(page_insert(boot_pgdir, p2, TEST_PAGE + PGSIZE, perm) == 0); assert((ptep = get_pte(boot_pgdir, TEST_PAGE + PGSIZE, 0)) != NULL); assert(ptep_u_read(ptep)); assert(ptep_u_write(ptep)); assert(ptep_u_read(&(boot_pgdir[PDX(TEST_PAGE)]))); assert(page_ref(p2) == 1); assert(page_insert(boot_pgdir, p1, TEST_PAGE + PGSIZE, 0) == 0); assert(page_ref(p1) == 2); assert(page_ref(p2) == 0); assert((ptep = get_pte(boot_pgdir, TEST_PAGE + PGSIZE, 0)) != NULL); assert(pa2page(*ptep) == p1); assert(!ptep_u_read(ptep)); page_remove(boot_pgdir, TEST_PAGE); assert(page_ref(p1) == 1); assert(page_ref(p2) == 0); page_remove(boot_pgdir, TEST_PAGE + PGSIZE); assert(page_ref(p1) == 0); assert(page_ref(p2) == 0); assert(page_ref(pa2page(boot_pgdir[PDX(TEST_PAGE)])) == 1); free_page(pa2page(boot_pgdir[PDX(TEST_PAGE)])); boot_pgdir[PDX(TEST_PAGE)] = 0; exit_range(boot_pgdir, TEST_PAGE, TEST_PAGE + PGSIZE); kprintf("check_pgdir() succeeded.\n"); }
/* use software emulated X86 pgfault */ static void handle_tlbmiss(struct trapframe* tf, int write) { #if 0 if(!trap_in_kernel(tf)){ print_trapframe(tf); while(1); } #endif static int entercnt = 0; entercnt ++; //kprintf("## enter handle_tlbmiss %d times\n", entercnt); int in_kernel = trap_in_kernel(tf); assert(current_pgdir != NULL); //print_trapframe(tf); uint32_t badaddr = tf->tf_vaddr; int ret = 0; pte_t *pte = get_pte(current_pgdir, tf->tf_vaddr, 0); if(pte==NULL || ptep_invalid(pte)){ //PTE miss, pgfault //panic("unimpl"); //TODO //tlb will not be refill in do_pgfault, //so a vmm pgfault will trigger 2 exception //permission check in tlb miss ret = pgfault_handler(tf, badaddr, get_error_code(write, pte)); }else{ //tlb miss only, reload it /* refill two slot */ /* check permission */ if(in_kernel){ tlb_refill(badaddr, pte); //kprintf("## refill K\n"); return; }else{ if(!ptep_u_read(pte)){ ret = -1; goto exit; } if(write && !ptep_u_write(pte)){ ret = -2; goto exit; } //kprintf("## refill U %d %08x\n", write, badaddr); tlb_refill(badaddr, pte); return ; } } exit: if(ret){ print_trapframe(tf); if(in_kernel){ panic("unhandled pgfault"); }else{ do_exit(-E_KILLED); } } return ; }
/* ucore use copy-on-write when forking a new process, * thus copy_range only copy pdt/pte and set their permission to * READONLY, a write will be handled in pgfault */ int copy_range(pgd_t *to, pgd_t *from, uintptr_t start, uintptr_t end, bool share) { assert(start % PGSIZE == 0 && end % PGSIZE == 0); assert(USER_ACCESS(start, end)); do { pte_t *ptep = get_pte(from, start, 0), *nptep; if (ptep == NULL) { if (get_pud(from, start, 0) == NULL) { start = ROUNDDOWN(start + PUSIZE, PUSIZE); } else if (get_pmd(from, start, 0) == NULL) { start = ROUNDDOWN(start + PMSIZE, PMSIZE); } else { start = ROUNDDOWN(start + PTSIZE, PTSIZE); } continue ; } if (*ptep != 0) { if ((nptep = get_pte(to, start, 1)) == NULL) { return -E_NO_MEM; } int ret; //kprintf("%08x %08x %08x\n", nptep, *nptep, start); assert(*ptep != 0 && *nptep == 0); #ifdef ARCH_ARM //TODO add code to handle swap if (ptep_present(ptep)){ //no body should be able to write this page //before a W-pgfault pte_perm_t perm = PTE_P; if(ptep_u_read(ptep)) perm |= PTE_U; if(!share){ //Original page should be set to readonly! //because Copy-on-write may happen //after the current proccess modifies its page ptep_set_perm(ptep, perm); }else{ if(ptep_u_write(ptep)){ perm |= PTE_W; } } struct Page *page = pte2page(*ptep); ret = page_insert(to, page, start, perm); } #else /* ARCH_ARM */ if (ptep_present(ptep)) { pte_perm_t perm = ptep_get_perm(ptep, PTE_USER); struct Page *page = pte2page(*ptep); if (!share && ptep_s_write(ptep)) { ptep_unset_s_write(&perm); pte_perm_t perm_with_swap_stat = ptep_get_perm(ptep, PTE_SWAP); ptep_set_perm(&perm_with_swap_stat, perm); page_insert(from, page, start, perm_with_swap_stat); } ret = page_insert(to, page, start, perm); assert(ret == 0); } #endif /* ARCH_ARM */ else { #ifdef CONFIG_NO_SWAP assert(0); #endif swap_entry_t entry; ptep_copy(&entry, ptep); swap_duplicate(entry); ptep_copy(nptep, &entry); } } start += PGSIZE; } while (start != 0 && start < end); #ifdef ARCH_ARM /* we have modified the PTE of the original * process, so invalidate TLB */ tlb_invalidate_all(); #endif return 0; }