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