diff options
Diffstat (limited to 'kernel/vm/pagefault.c')
-rw-r--r-- | kernel/vm/pagefault.c | 75 |
1 files changed, 74 insertions, 1 deletions
diff --git a/kernel/vm/pagefault.c b/kernel/vm/pagefault.c index 764ce85..2e0c92d 100644 --- a/kernel/vm/pagefault.c +++ b/kernel/vm/pagefault.c @@ -49,5 +49,78 @@ void handle_pagefault(uintptr_t vaddr, uintptr_t cause) { dbg(DBG_VM, "vaddr = 0x%p (0x%p), cause = %lu\n", (void *)vaddr, PAGE_ALIGN_DOWN(vaddr), cause); - NOT_YET_IMPLEMENTED("VM: handle_pagefault"); + // NOT_YET_IMPLEMENTED("VM: handle_pagefault"); + + // 1) Find the vmarea that contains vaddr, if it exists. + // check that the vaddr is valid + if (vaddr < USER_MEM_LOW || vaddr > USER_MEM_HIGH) + { + do_exit(EFAULT); + } + // lookup the vmarea for this addr + vmarea_t *vma = vmmap_lookup(curproc->p_vmmap, ADDR_TO_PN(vaddr)); + if (vma == NULL) + { + do_exit(EFAULT); + } + + // 2) Check the vmarea's protections (see the vmarea_t struct) against the 'cause' + // error out if the fault has cause write and we don't have write permission in the area + if ((cause & FAULT_WRITE) && !(vma->vma_prot & PROT_WRITE)) + { + do_exit(EFAULT); + } + // error out if the fault has cause exec and we don't have exec permission in the area + if ((cause & FAULT_EXEC) && !(vma->vma_prot & PROT_EXEC)) + { + do_exit(EFAULT); + } + // error out if we don't have read permission in the area + if (!(vma->vma_prot & PROT_READ)) + { + do_exit(EFAULT); + } + // error our if we don't have any permission in the area + if (vma->vma_prot == PROT_NONE) + { + do_exit(EFAULT); + } + + // 3) Obtain the corresponding pframe from the vmarea's mobj. + pframe_t *pf; + mobj_lock(vma->vma_obj); + int ret = mobj_get_pframe( + vma->vma_obj, + vma->vma_off + (ADDR_TO_PN(vaddr) - vma->vma_start), + cause & FAULT_WRITE ? 1 : 0, + &pf + ); + mobj_unlock(vma->vma_obj); + if (ret < 0) + { + do_exit(EFAULT); + } + + // 4) Finally, set up a call to pt_map to insert a new mapping into the appropriate pagetable + int pdflags = PT_PRESENT | PT_WRITE | PT_USER; + int ptflags = PT_PRESENT | PT_USER; + if (cause & FAULT_WRITE) + { + ptflags |= PT_WRITE; + } + + int err = pt_map( + curproc->p_pml4, + pt_virt_to_phys((uintptr_t) pf->pf_addr), + (uintptr_t) PAGE_ALIGN_DOWN(vaddr), + pdflags, + ptflags + ); + if (err < 0) + { + do_exit(EFAULT); + } + + // 5) Flush the TLB + tlb_flush((uintptr_t) PAGE_ALIGN_DOWN(vaddr)); } |