1 #include <lunaix/mm/fault.h>
2 #include <lunaix/mm/pmm.h>
3 #include <lunaix/mm/region.h>
4 #include <lunaix/mm/vmm.h>
5 #include <lunaix/sched.h>
6 #include <lunaix/signal.h>
7 #include <lunaix/status.h>
8 #include <lunaix/syslog.h>
9 #include <lunaix/trace.h>
10 #include <lunaix/pcontext.h>
12 #include <sys/mm/mm_defs.h>
14 #include <klibc/string.h>
19 __gather_memaccess_info(struct fault_context* context)
21 pte_t* ptep = (pte_t*)context->fault_va;
22 ptr_t mnt = ptep_vm_mnt(ptep);
25 context->mm = vmspace(__current);
27 if (mnt < VMS_MOUNT_1) {
32 context->ptep_fault = true;
33 context->remote_fault = (mnt != VMS_SELF);
35 if (context->remote_fault && context->mm) {
36 context->mm = context->mm->guest_mm;
41 ptep = (pte_t*)page_addr(ptep_pfn(ptep));
42 mnt = ptep_vm_mnt(ptep);
43 if (mnt < VMS_MOUNT_1) {
50 ptep = (pte_t*)page_addr(ptep_pfn(ptep));
51 mnt = ptep_vm_mnt(ptep);
52 if (mnt < VMS_MOUNT_1) {
59 ptep = (pte_t*)page_addr(ptep_pfn(ptep));
60 mnt = ptep_vm_mnt(ptep);
61 if (mnt < VMS_MOUNT_1) {
67 ptep = (pte_t*)page_addr(ptep_pfn(ptep));
68 mnt = ptep_vm_mnt(ptep);
70 assert(mnt < VMS_MOUNT_1);
74 context->fault_refva = refva;
78 __prepare_fault_context(struct fault_context* fault)
80 if (!__arch_prepare_fault_context(fault)) {
84 __gather_memaccess_info(fault);
86 pte_t* fault_ptep = fault->fault_ptep;
87 ptr_t fault_va = fault->fault_va;
88 pte_t fault_pte = *fault_ptep;
89 bool kernel_vmfault = kernel_addr(fault_va);
90 bool kernel_refaddr = kernel_addr(fault->fault_refva);
92 // for a ptep fault, the parent page tables should match the actual
93 // accesser permission
95 ptep_alloc_hierarchy(fault_ptep, fault_va, KERNEL_DATA);
97 ptep_alloc_hierarchy(fault_ptep, fault_va, USER_DATA);
100 fault->fault_pte = fault_pte;
102 if (fault->ptep_fault && !kernel_refaddr) {
103 fault->resolving = pte_setprot(fault_pte, USER_DATA);
105 fault->resolving = pte_setprot(fault_pte, KERNEL_DATA);
108 fault->kernel_vmfault = kernel_vmfault;
109 fault->kernel_access = kernel_context(fault->ictx);
115 __handle_conflict_pte(struct fault_context* fault)
117 pte_t pte = fault->fault_pte;
118 ptr_t fault_pa = pte_paddr(pte);
119 if (!pte_allow_user(pte)) {
123 assert(pte_iswprotect(pte));
125 if (writable_region(fault->vmr)) {
126 // normal page fault, do COW
127 // TODO makes `vmm_dup_page` arch-independent
128 ptr_t pa = (ptr_t)vmm_dup_page(fault_pa);
130 pmm_free_page(fault_pa);
131 pte_t new_pte = pte_setpaddr(pte, pa);
132 new_pte = pte_mkwritable(new_pte);
134 fault_resolved(fault, new_pte, NO_PREALLOC);
142 __handle_anon_region(struct fault_context* fault)
144 pte_t pte = fault->resolving;
145 pte_attr_t prot = region_pteprot(fault->vmr);
146 pte = pte_setprot(pte, prot);
148 fault_resolved(fault, pte, 0);
153 __handle_named_region(struct fault_context* fault)
155 struct mm_region* vmr = fault->vmr;
156 struct v_file* file = vmr->mfile;
158 pte_t pte = fault->resolving;
159 ptr_t fault_va = va_align(fault->fault_va);
161 u32_t mseg_off = (fault_va - vmr->start);
162 u32_t mfile_off = mseg_off + vmr->foff;
164 int errno = file->ops->read_page(file->inode, (void*)fault_va, mfile_off);
166 ERROR("fail to populate page (%d)", errno);
170 pte_attr_t prot = region_pteprot(vmr);
171 pte = pte_setprot(pte, prot);
173 fault_resolved(fault, pte, 0);
177 __handle_kernel_page(struct fault_context* fault)
179 // we must ensure only ptep fault is resolvable
180 if (fault->fault_va < VMS_MOUNT_1) {
184 fault_resolved(fault, fault->resolving, 0);
185 pmm_set_attr(fault->prealloc_pa, PP_FGPERSIST);
190 fault_prealloc_page(struct fault_context* fault)
192 if (!pte_isnull(fault->fault_pte)) {
198 pte = vmm_alloc_page(fault->fault_ptep, fault->resolving);
199 if (pte_isnull(pte)) {
203 fault->resolving = pte;
204 fault->prealloc_pa = pte_paddr(fault->resolving);
206 pmm_set_attr(fault->prealloc_pa, 0);
207 cpu_flush_page(fault->fault_va);
212 __fail_to_resolve(struct fault_context* fault)
214 if (fault->prealloc_pa) {
215 pmm_free_page(fault->prealloc_pa);
218 ERROR("(pid: %d) Segmentation fault on %p (%p,e=0x%x)",
224 trace_printstack_isr(fault->ictx);
226 if (fault->kernel_access) {
227 // if a page fault from kernel is not resolvable, then
228 // something must be went south
229 FATAL("unresolvable page fault");
233 thread_setsignal(current_thread, _SIGSEGV);
236 fail("Unexpected return from segfault");
242 __try_resolve_fault(struct fault_context* fault)
244 pte_t fault_pte = fault->fault_pte;
245 if (pte_isguardian(fault_pte)) {
246 ERROR("memory region over-running");
250 if (fault->kernel_vmfault && fault->kernel_access) {
251 __handle_kernel_page(fault);
256 vm_regions_t* vmr = &fault->mm->regions;
257 fault->vmr = region_get(vmr, fault->fault_va);
263 if (pte_isloaded(fault_pte)) {
264 __handle_conflict_pte(fault);
266 else if (anon_region(fault->vmr)) {
267 __handle_anon_region(fault);
269 else if (fault->vmr->mfile) {
270 __handle_named_region(fault);
273 // page not present, might be a chance to introduce swap file?
274 ERROR("WIP page fault route");
278 return !!(fault->resolve_type & RESOLVE_OK);
282 intr_routine_page_fault(const isr_param* param)
284 if (param->depth > 10) {
285 // Too many nested fault! we must messed up something
286 // XXX should we failed silently?
290 struct fault_context fault = { .ictx = param };
292 if (!__prepare_fault_context(&fault)) {
293 __fail_to_resolve(&fault);
296 fault_prealloc_page(&fault);
298 if (!__try_resolve_fault(&fault)) {
299 __fail_to_resolve(&fault);
302 if ((fault.resolve_type & NO_PREALLOC)) {
303 if (fault.prealloc_pa) {
304 pmm_free_page(fault.prealloc_pa);
308 set_pte(fault.fault_ptep, fault.resolving);
310 cpu_flush_page(fault.fault_va);
311 cpu_flush_page((ptr_t)fault.fault_ptep);