Code-base clean-up and refactoring (#47)
[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 <asm/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 void
81 __prepare_fault_context(struct fault_context* fault)
82 {
83     pte_t* fault_ptep      = fault->fault_ptep;
84     ptr_t  fault_va        = fault->fault_va;
85     pte_t  fault_pte       = *fault_ptep;
86     bool   kernel_vmfault  = kernel_addr(fault_va);
87     bool   kernel_refaddr  = kernel_addr(fault->fault_refva);
88     
89     // for a ptep fault, the parent page tables should match the actual
90     //  accesser permission
91     if (kernel_refaddr) {
92         ptep_alloc_hierarchy(fault_ptep, fault_va, KERNEL_PGTAB);
93     } else {
94         ptep_alloc_hierarchy(fault_ptep, fault_va, USER_PGTAB);
95     }
96
97     fault->fault_pte = fault_pte;
98
99     if (fault->ptep_fault) {
100         // fault on intermediate levels.
101         fault_pte = pte_setprot(fault_pte, KERNEL_PGTAB);
102     }
103     
104     if (!kernel_refaddr) {
105         fault_pte = pte_mkuser(fault_pte);
106     }
107
108     fault->resolving = pte_mkloaded(fault_pte);
109     fault->kernel_vmfault = kernel_vmfault;
110     fault->kernel_access  = kernel_context(fault->hstate);
111 }
112
113 static inline void
114 __flush_staled_tlb(struct fault_context* fault, struct leaflet* leaflet)
115 {
116     tlb_flush_mm_range(fault->mm, fault->fault_va, leaflet_nfold(leaflet));
117 }
118
119 static void
120 __handle_conflict_pte(struct fault_context* fault) 
121 {
122     pte_t pte;
123     struct leaflet *fault_leaflet, *duped_leaflet;
124
125     pte = fault->fault_pte;
126     fault_leaflet = pte_leaflet(pte);
127     
128     if (!pte_allow_user(pte)) {
129         return;
130     }
131
132     assert(pte_iswprotect(pte));
133
134     if (writable_region(fault->vmr)) {
135         // normal page fault, do COW
136         duped_leaflet = dup_leaflet(fault_leaflet);
137
138         pte = pte_mkwritable(pte);
139         pte = pte_mkuntouch(pte);
140         pte = pte_mkclean(pte);
141
142         ptep_map_leaflet(fault->fault_ptep, pte, duped_leaflet);
143         __flush_staled_tlb(fault, duped_leaflet);
144
145         leaflet_return(fault_leaflet);
146
147         fault_resolved(fault, NO_PREALLOC);
148     }
149
150     return;
151 }
152
153
154 static void
155 __handle_anon_region(struct fault_context* fault)
156 {
157     pte_t pte = fault->resolving;
158     pte = region_tweakpte(fault->vmr, pte);
159
160     // TODO Potentially we can get different order of leaflet here
161     struct leaflet* region_part = alloc_leaflet(0);
162     
163     ptep_map_leaflet(fault->fault_ptep, pte, region_part);
164     __flush_staled_tlb(fault, region_part);
165
166     fault_resolved(fault, NO_PREALLOC);
167 }
168
169
170 static void
171 __handle_named_region(struct fault_context* fault)
172 {
173     int errno = 0;
174     struct mm_region* vmr = fault->vmr;
175     struct v_file* file = vmr->mfile;
176     struct v_file_ops * fops = file->ops;
177
178     pte_t pte       = fault->resolving;
179     ptr_t fault_va  = page_aligned(fault->fault_va);
180
181     u32_t mseg_off  = (fault_va - vmr->start);
182     u32_t mfile_off = mseg_off + vmr->foff;
183     size_t mapped_len = vmr->flen;
184
185     // TODO Potentially we can get different order of leaflet here
186     struct leaflet* region_part = alloc_leaflet(0);
187
188     pte = region_tweakpte(vmr, pte);
189     ptep_map_leaflet(fault->fault_ptep, pte, region_part);
190
191     if (mseg_off < mapped_len) {
192         mapped_len = MIN(mapped_len - mseg_off, PAGE_SIZE);
193     }
194     else {
195         mapped_len = 0;
196     }
197
198     if (mapped_len == PAGE_SIZE) {
199         errno = fops->read_page(file->inode, (void*)fault_va, mfile_off);
200     }
201     else {
202         leaflet_wipe(region_part);
203         
204         if (mapped_len) {
205             errno = fops->read(file->inode, 
206                     (void*)fault_va, mapped_len, mfile_off);
207         }
208     }
209
210     if (errno < 0) {
211         ERROR("fail to populate page (%d)", errno);
212
213         ptep_unmap_leaflet(fault->fault_ptep, region_part);
214         leaflet_return(region_part);
215
216         return;
217     }
218
219     __flush_staled_tlb(fault, region_part);
220
221     fault_resolved(fault, NO_PREALLOC);
222 }
223
224 static void
225 __handle_kernel_page(struct fault_context* fault)
226 {
227     // we must ensure only ptep fault is resolvable
228     if (!is_ptep(fault->fault_va)) {
229         return;
230     }
231     
232     struct leaflet* leaflet = fault->prealloc;
233
234     pin_leaflet(leaflet);
235     leaflet_wipe(leaflet);
236     
237     pte_t pte = fault->resolving;
238     ptep_map_leaflet(fault->fault_ptep, pte, leaflet);
239
240     tlb_flush_kernel_ranged(fault->fault_va, leaflet_nfold(leaflet));
241
242     fault_resolved(fault, 0);
243 }
244
245
246 static void
247 fault_prealloc_page(struct fault_context* fault)
248 {
249     if (!pte_isnull(fault->fault_pte)) {
250         return;
251     }
252
253     pte_t pte;
254     
255     struct leaflet* leaflet = alloc_leaflet(0);
256     if (!leaflet) {
257         return;
258     }
259
260     fault->prealloc = leaflet;
261 }
262
263
264 void noret
265 fault_resolving_failed(struct fault_context* fault)
266 {
267     if (fault->prealloc) {
268         leaflet_return(fault->prealloc);
269     }
270
271     ERROR("(pid: %d) Segmentation fault on %p (%p,e=0x%x)",
272           __current->pid,
273           fault->fault_va,
274           fault->fault_instn,
275           fault->fault_data);
276
277
278     if (fault->kernel_access) {
279         // if a page fault from kernel is not resolvable, then
280         //  something must be went south
281         FATAL("unresolvable page fault");
282         failsafe_diagnostic();
283     }
284
285     trace_printstack_isr(fault->hstate);
286     
287     thread_setsignal(current_thread, _SIGSEGV);
288
289     schedule();
290     fail("Unexpected return from segfault");
291
292     unreachable;
293 }
294
295 static bool
296 __try_resolve_fault(struct fault_context* fault)
297 {
298     pte_t fault_pte = fault->fault_pte;
299     if (pte_isguardian(fault_pte)) {
300         ERROR("memory region over-running");
301         return false;
302     }
303
304     if (fault->kernel_vmfault && fault->kernel_access) {
305         __handle_kernel_page(fault);
306         goto done;
307     }
308
309     assert(fault->mm);
310     vm_regions_t* vmr = &fault->mm->regions;
311     fault->vmr = region_get(vmr, fault->fault_va);
312
313     if (!fault->vmr) {
314         return false;
315     }
316
317     if (pte_isloaded(fault_pte)) {
318         __handle_conflict_pte(fault);
319     }
320     else if (anon_region(fault->vmr)) {
321         __handle_anon_region(fault);
322     }
323     else if (fault->vmr->mfile) {
324         __handle_named_region(fault);
325     }
326     else {
327         // page not present, might be a chance to introduce swap file?
328         ERROR("WIP page fault route");
329     }
330     
331 done:
332     return !!(fault->resolve_type & RESOLVE_OK);
333 }
334
335 bool
336 handle_page_fault(struct fault_context* fault)
337 {
338     __gather_memaccess_info(fault);
339     __prepare_fault_context(fault);
340
341     fault_prealloc_page(fault);
342
343     if (!__try_resolve_fault(fault)) {
344         return false;
345     }
346
347     if ((fault->resolve_type & NO_PREALLOC)) {
348         if (fault->prealloc) {
349             leaflet_return(fault->prealloc);
350         }
351     }
352
353     tlb_flush_kernel(fault->fault_va);
354     return true;
355 }