Boot framework rework (#45)
[lunaix-os.git] / lunaix-os / kernel / mm / fault.c
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/hart_state.h>
11 #include <lunaix/failsafe.h>
12
13 #include <sys/mm/mm_defs.h>
14
15 #include <klibc/string.h>
16
17 LOG_MODULE("pf")
18
19 static void
20 __gather_memaccess_info(struct fault_context* context)
21 {
22     pte_t* ptep = (pte_t*)context->fault_va;
23     ptr_t mnt = ptep_vm_mnt(ptep);
24     ptr_t refva;
25     
26     context->mm = vmspace(__current);
27
28     if (!vmnt_packed(ptep)) {
29         refva = (ptr_t)ptep;
30         goto done;
31     }
32
33     context->ptep_fault = true;
34     context->remote_fault = !active_vms(mnt);
35     
36     if (context->remote_fault && context->mm) {
37         context->mm = context->mm->guest_mm;
38         assert(context->mm);
39     }
40
41     // unpack the ptep to reveal the one true va!
42
43 #if LnT_ENABLED(1)
44     ptep = (pte_t*)page_addr(ptep_pfn(ptep));
45     mnt  = ptep_vm_mnt(ptep);
46     if (!vmnt_packed(ptep)) {
47         refva = (ptr_t)ptep;
48         goto done;
49     }
50 #endif
51
52 #if LnT_ENABLED(2)
53     ptep = (pte_t*)page_addr(ptep_pfn(ptep));
54     mnt  = ptep_vm_mnt(ptep);
55     if (!vmnt_packed(ptep)) {
56         refva = (ptr_t)ptep;
57         goto done;
58     }
59 #endif
60
61 #if LnT_ENABLED(3)
62     ptep = (pte_t*)page_addr(ptep_pfn(ptep));
63     mnt  = ptep_vm_mnt(ptep);
64     if (!vmnt_packed(ptep)) {
65         refva = (ptr_t)ptep;
66         goto done;
67     }
68 #endif
69
70     ptep = (pte_t*)page_addr(ptep_pfn(ptep));
71     mnt  = ptep_vm_mnt(ptep);
72     
73     assert(!vmnt_packed(ptep));
74     refva = (ptr_t)ptep;
75
76 done:
77     context->fault_refva = refva;
78 }
79
80 static bool
81 __prepare_fault_context(struct fault_context* fault)
82 {
83     if (!__arch_prepare_fault_context(fault)) {
84         return false;
85     }
86
87     __gather_memaccess_info(fault);
88
89     pte_t* fault_ptep      = fault->fault_ptep;
90     ptr_t  fault_va        = fault->fault_va;
91     pte_t  fault_pte       = *fault_ptep;
92     bool   kernel_vmfault  = kernel_addr(fault_va);
93     bool   kernel_refaddr  = kernel_addr(fault->fault_refva);
94     
95     // for a ptep fault, the parent page tables should match the actual
96     //  accesser permission
97     if (kernel_refaddr) {
98         ptep_alloc_hierarchy(fault_ptep, fault_va, KERNEL_PGTAB);
99     } else {
100         ptep_alloc_hierarchy(fault_ptep, fault_va, USER_PGTAB);
101     }
102
103     fault->fault_pte = fault_pte;
104
105     if (fault->ptep_fault) {
106         // fault on intermediate levels.
107         fault_pte = pte_setprot(fault_pte, KERNEL_PGTAB);
108     }
109     
110     if (!kernel_refaddr) {
111         fault_pte = pte_mkuser(fault_pte);
112     }
113
114     fault->resolving = pte_mkloaded(fault_pte);
115     fault->kernel_vmfault = kernel_vmfault;
116     fault->kernel_access  = kernel_context(fault->hstate);
117
118     return true;
119 }
120
121 static inline void
122 __flush_staled_tlb(struct fault_context* fault, struct leaflet* leaflet)
123 {
124     tlb_flush_mm_range(fault->mm, fault->fault_va, leaflet_nfold(leaflet));
125 }
126
127 static void
128 __handle_conflict_pte(struct fault_context* fault) 
129 {
130     pte_t pte;
131     struct leaflet *fault_leaflet, *duped_leaflet;
132
133     pte = fault->fault_pte;
134     fault_leaflet = pte_leaflet(pte);
135     
136     if (!pte_allow_user(pte)) {
137         return;
138     }
139
140     assert(pte_iswprotect(pte));
141
142     if (writable_region(fault->vmr)) {
143         // normal page fault, do COW
144         duped_leaflet = dup_leaflet(fault_leaflet);
145
146         pte = pte_mkwritable(pte);
147         pte = pte_mkuntouch(pte);
148         pte = pte_mkclean(pte);
149
150         ptep_map_leaflet(fault->fault_ptep, pte, duped_leaflet);
151         __flush_staled_tlb(fault, duped_leaflet);
152
153         leaflet_return(fault_leaflet);
154
155         fault_resolved(fault, NO_PREALLOC);
156     }
157
158     return;
159 }
160
161
162 static void
163 __handle_anon_region(struct fault_context* fault)
164 {
165     pte_t pte = fault->resolving;
166     pte = region_tweakpte(fault->vmr, pte);
167
168     // TODO Potentially we can get different order of leaflet here
169     struct leaflet* region_part = alloc_leaflet(0);
170     
171     ptep_map_leaflet(fault->fault_ptep, pte, region_part);
172     __flush_staled_tlb(fault, region_part);
173
174     fault_resolved(fault, NO_PREALLOC);
175 }
176
177
178 static void
179 __handle_named_region(struct fault_context* fault)
180 {
181     int errno = 0;
182     struct mm_region* vmr = fault->vmr;
183     struct v_file* file = vmr->mfile;
184     struct v_file_ops * fops = file->ops;
185
186     pte_t pte       = fault->resolving;
187     ptr_t fault_va  = page_aligned(fault->fault_va);
188
189     u32_t mseg_off  = (fault_va - vmr->start);
190     u32_t mfile_off = mseg_off + vmr->foff;
191     size_t mapped_len = vmr->flen;
192
193     // TODO Potentially we can get different order of leaflet here
194     struct leaflet* region_part = alloc_leaflet(0);
195
196     pte = region_tweakpte(vmr, pte);
197     ptep_map_leaflet(fault->fault_ptep, pte, region_part);
198
199     if (mseg_off < mapped_len) {
200         mapped_len = MIN(mapped_len - mseg_off, PAGE_SIZE);
201     }
202     else {
203         mapped_len = 0;
204     }
205
206     if (mapped_len == PAGE_SIZE) {
207         errno = fops->read_page(file->inode, (void*)fault_va, mfile_off);
208     }
209     else {
210         leaflet_wipe(region_part);
211         
212         if (mapped_len) {
213             errno = fops->read(file->inode, 
214                     (void*)fault_va, mapped_len, mfile_off);
215         }
216     }
217
218     if (errno < 0) {
219         ERROR("fail to populate page (%d)", errno);
220
221         ptep_unmap_leaflet(fault->fault_ptep, region_part);
222         leaflet_return(region_part);
223
224         return;
225     }
226
227     __flush_staled_tlb(fault, region_part);
228
229     fault_resolved(fault, NO_PREALLOC);
230 }
231
232 static void
233 __handle_kernel_page(struct fault_context* fault)
234 {
235     // we must ensure only ptep fault is resolvable
236     if (!is_ptep(fault->fault_va)) {
237         return;
238     }
239     
240     struct leaflet* leaflet = fault->prealloc;
241
242     pin_leaflet(leaflet);
243     leaflet_wipe(leaflet);
244     
245     pte_t pte = fault->resolving;
246     ptep_map_leaflet(fault->fault_ptep, pte, leaflet);
247
248     tlb_flush_kernel_ranged(fault->fault_va, leaflet_nfold(leaflet));
249
250     fault_resolved(fault, 0);
251 }
252
253
254 static void
255 fault_prealloc_page(struct fault_context* fault)
256 {
257     if (!pte_isnull(fault->fault_pte)) {
258         return;
259     }
260
261     pte_t pte;
262     
263     struct leaflet* leaflet = alloc_leaflet(0);
264     if (!leaflet) {
265         return;
266     }
267
268     fault->prealloc = leaflet;
269 }
270
271
272 static void noret
273 __fail_to_resolve(struct fault_context* fault)
274 {
275     if (fault->prealloc) {
276         leaflet_return(fault->prealloc);
277     }
278
279     ERROR("(pid: %d) Segmentation fault on %p (%p,e=0x%x)",
280           __current->pid,
281           fault->fault_va,
282           fault->fault_instn,
283           fault->fault_data);
284
285
286     if (fault->kernel_access) {
287         // if a page fault from kernel is not resolvable, then
288         //  something must be went south
289         FATAL("unresolvable page fault");
290         failsafe_diagnostic();
291     }
292
293     trace_printstack_isr(fault->hstate);
294     
295     thread_setsignal(current_thread, _SIGSEGV);
296
297     schedule();
298     fail("Unexpected return from segfault");
299
300     unreachable;
301 }
302
303 static bool
304 __try_resolve_fault(struct fault_context* fault)
305 {
306     pte_t fault_pte = fault->fault_pte;
307     if (pte_isguardian(fault_pte)) {
308         ERROR("memory region over-running");
309         return false;
310     }
311
312     if (fault->kernel_vmfault && fault->kernel_access) {
313         __handle_kernel_page(fault);
314         goto done;
315     }
316
317     assert(fault->mm);
318     vm_regions_t* vmr = &fault->mm->regions;
319     fault->vmr = region_get(vmr, fault->fault_va);
320
321     if (!fault->vmr) {
322         return false;
323     }
324
325     if (pte_isloaded(fault_pte)) {
326         __handle_conflict_pte(fault);
327     }
328     else if (anon_region(fault->vmr)) {
329         __handle_anon_region(fault);
330     }
331     else if (fault->vmr->mfile) {
332         __handle_named_region(fault);
333     }
334     else {
335         // page not present, might be a chance to introduce swap file?
336         ERROR("WIP page fault route");
337     }
338     
339 done:
340     return !!(fault->resolve_type & RESOLVE_OK);
341 }
342
343 void
344 intr_routine_page_fault(const struct hart_state* hstate)
345 {
346     if (hstate->depth > 10) {
347         // Too many nested fault! we must messed up something
348         // XXX should we failed silently?
349         spin();
350     }
351
352     struct fault_context fault = { .hstate = hstate };
353
354     if (!__prepare_fault_context(&fault)) {
355         __fail_to_resolve(&fault);
356     }
357
358     fault_prealloc_page(&fault);
359
360     if (!__try_resolve_fault(&fault)) {
361         __fail_to_resolve(&fault);
362     }
363
364     if ((fault.resolve_type & NO_PREALLOC)) {
365         if (fault.prealloc) {
366             leaflet_return(fault.prealloc);
367         }
368     }
369
370     tlb_flush_kernel(fault.fault_va);
371 }