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