0b7ebfc8386e066d58f546be14a7bd8bec1a6421
[barrelfish] / kernel / arch / x86_32 / page_mappings_arch.c
1 /**
2  * \file
3  * \brief
4  */
5
6 /*
7  * Copyright (c) 2010-2013 ETH Zurich.
8  * All rights reserved.
9  *
10  * This file is distributed under the terms in the attached LICENSE file.
11  * If you do not find this file, copies can be found by writing to:
12  * ETH Zurich D-INFK, Universitaetstr. 6, CH-8092 Zurich. Attn: Systems Group.
13  */
14
15 #include <kernel.h>
16 #include <dispatch.h>
17 #include <target/x86_32/paging_kernel_target.h>
18 #include <target/x86_32/offsets_target.h>
19 #include <paging_kernel_arch.h>
20 #include <string.h>
21 #include <cap_predicates.h>
22
23 static inline struct cte *cte_for_cap(struct capability *cap)
24 {
25     return (struct cte *) (cap - offsetof(struct cte, cap));
26 }
27
28 #ifdef CONFIG_PAE
29 /// Map within a x86_32 pdpt
30 static errval_t x86_32_pdpt(struct capability *dest, cslot_t slot,
31                             struct capability * src, uintptr_t flags,
32                             uintptr_t offset, uintptr_t pte_count)
33 {
34     if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
35         return SYS_ERR_VNODE_SLOT_INVALID;
36     }
37
38     if (pte_count > 1) { // disallow multiple pdpt mappings at a time
39         return SYS_ERR_VM_MAP_SIZE;
40     }
41
42     if (src->type != ObjType_VNode_x86_32_pdir) { // Right mapping
43         return SYS_ERR_WRONG_MAPPING;
44     }
45
46     if(slot >= X86_32_PDPTE_BASE(X86_32_MEMORY_OFFSET)) { // Kernel mapped here
47         return SYS_ERR_VNODE_SLOT_RESERVED;
48     }
49
50     // Destination
51     genpaddr_t dest_gp   = dest->u.vnode_x86_32_pdpt.base;
52     lpaddr_t dest_lp     = gen_phys_to_local_phys(dest_gp);
53     lvaddr_t dest_lv     = local_phys_to_mem(dest_lp);
54     union x86_32_pdpte_entry *entry =
55         (union x86_32_pdpte_entry *)dest_lv + slot;
56
57     // Set metadata
58     struct cte *src_cte = cte_for_cap(src);
59     src_cte->mapping_info.pte = dest_lp + slot * sizeof(union x86_32_pdpte_entry);
60     src_cte->mapping_info.pte_count = pte_count;
61     src_cte->mapping_info.offset = offset;
62
63     // Source
64     genpaddr_t src_gp   = src->u.vnode_x86_32_pdir.base;
65     lpaddr_t src_lp     = gen_phys_to_local_phys(src_gp);
66     paging_x86_32_map_pdpte(entry, src_lp);
67     paging_x86_32_context_switch(dcb_current->vspace); // To flush TLB
68
69     return SYS_ERR_OK;
70 }
71 #endif
72
73 /// Map within a x86_32 pdir
74 static errval_t x86_32_pdir(struct capability *dest, cslot_t slot,
75                             struct capability * src, uintptr_t flags,
76                             uintptr_t offset, uintptr_t pte_count)
77 {
78     //printf("x86_32_pdir\n");
79     if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
80         return SYS_ERR_VNODE_SLOT_INVALID;
81     }
82
83     if (slot + pte_count > X86_32_PTABLE_SIZE) { // disallow more than one page at a time
84         return SYS_ERR_VM_MAP_SIZE;
85     }
86
87 #ifndef CONFIG_PAE
88     if(slot >= X86_32_PDIR_BASE(X86_32_MEMORY_OFFSET)) { // Kernel mapped here
89         return SYS_ERR_VNODE_SLOT_RESERVED;
90     }
91 #endif
92
93     //TODO large page code
94     size_t page_size = X86_32_LARGE_PAGE_SIZE;
95     if(dest->type == ObjType_VNode_x86_32_pdir &&
96        src->type != ObjType_VNode_x86_32_ptable)
97     {
98         printf("\tlarge page\n");
99         cslot_t last_slot = slot + pte_count;
100
101         if (src->type != ObjType_Frame &&
102             src->type != ObjType_DevFrame) { // Right mapping
103             printf("\tlarge page wrong mapping\n");
104             return SYS_ERR_WRONG_MAPPING;
105         }
106
107         // check offset within frame
108         if (offset + pte_count * page_size > get_size(src)) {
109             printf("\tlarge page wrong offset\n");
110             return SYS_ERR_FRAME_OFFSET_INVALID;
111         }
112
113         printf("calc new flags\n");
114         /* Calculate page access protection flags */
115         // Get frame cap rights
116         paging_x86_32_flags_t flags_large =
117             paging_x86_32_cap_to_page_flags(src->rights);
118         // Mask with provided access rights mask
119         flags_large = paging_x86_32_mask_attrs(flags_large, X86_32_PTABLE_ACCESS(flags));
120         // Add additional arch-specific flags
121         flags_large |= X86_32_PTABLE_FLAGS(flags);
122         // Unconditionally mark the page present
123         flags_large |= X86_32_PTABLE_PRESENT;
124     
125         printf("calc addrs\n");
126         // Convert destination base address
127         genpaddr_t dest_gp   = get_address(dest);
128         lpaddr_t dest_lp     = gen_phys_to_local_phys(dest_gp);
129         lvaddr_t dest_lv     = local_phys_to_mem(dest_lp);
130         // Convert source base address
131         genpaddr_t src_gp   = get_address(src);
132         lpaddr_t src_lp     = gen_phys_to_local_phys(src_gp);
133         // Set metadata
134         printf("set metadata\n");
135         struct cte *src_cte = cte_for_cap(src);
136         src_cte->mapping_info.pte = dest_lp + slot * sizeof(union x86_32_ptable_entry);
137         src_cte->mapping_info.pte_count = pte_count;
138         src_cte->mapping_info.offset = offset;
139     
140
141         printf("start looping\n");
142         for (; slot < last_slot; slot++, offset += page_size) {
143             printf("looping\n");
144             union x86_32_ptable_entry *entry =
145                 (union x86_32_ptable_entry *)dest_lv + slot;
146                 
147             /* FIXME: Flush TLB if the page is already present
148              * in the meantime, since we don't do this, we just assert that
149              * we never reuse a VA mapping */
150             if (X86_32_IS_PRESENT(entry)) {
151                 printf("Trying to map into an already present page NYI.\n");
152                 return SYS_ERR_VNODE_SLOT_INUSE;
153             }
154             
155             printf("map it\n");
156             // Carry out the page mapping
157             paging_x86_32_map_large(entry, src_lp + offset, flags_large);
158         }
159
160         printf("mapping complete\n");
161         return SYS_ERR_OK;
162     }
163
164     if (src->type != ObjType_VNode_x86_32_ptable) { // Right mapping
165         printf("error4\n");
166         return SYS_ERR_WRONG_MAPPING;
167     }
168
169     // Destination
170     genpaddr_t dest_gp   = dest->u.vnode_x86_32_pdir.base;
171     lpaddr_t dest_lp     = gen_phys_to_local_phys(dest_gp);
172     lvaddr_t dest_lv     = local_phys_to_mem(dest_lp);
173     union x86_32_pdir_entry *entry =
174         (union x86_32_pdir_entry *)dest_lv + slot;
175
176     // Set metadata
177     struct cte *src_cte = cte_for_cap(src);
178     src_cte->mapping_info.pte = dest_lp + slot * sizeof(union x86_32_pdir_entry);
179     src_cte->mapping_info.pte_count = pte_count;
180     src_cte->mapping_info.offset = offset;
181
182
183     // Source
184     // XXX: offset is ignored
185     genpaddr_t src_gp   = src->u.vnode_x86_32_pdir.base;
186     lpaddr_t src_lp     = gen_phys_to_local_phys(src_gp);
187     paging_x86_32_map_table(entry, src_lp);
188
189     return SYS_ERR_OK;
190 }
191
192 /// Map within a x86_32 ptable
193 static errval_t x86_32_ptable(struct capability *dest, cslot_t slot,
194                               struct capability * src, uintptr_t uflags,
195                               uintptr_t offset, uintptr_t pte_count)
196 {
197     //printf("x86_32_ptable\n");
198     if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
199         return SYS_ERR_VNODE_SLOT_INVALID;
200     }
201
202     cslot_t last_slot = slot + pte_count;
203
204     if (last_slot > X86_32_PTABLE_SIZE) {
205         printf("slot = %"PRIuCSLOT", last_slot = %"PRIuCSLOT", PTABLE_SIZE = %d\n", slot, last_slot, X86_32_PTABLE_SIZE);
206         return SYS_ERR_VM_MAP_SIZE;
207     }
208
209     if (src->type != ObjType_Frame &&
210         src->type != ObjType_DevFrame) { // Right mapping
211         return SYS_ERR_WRONG_MAPPING;
212     }
213
214     // check offset within frame
215     if (offset + pte_count * X86_32_BASE_PAGE_SIZE > get_size(src)) {
216         return SYS_ERR_FRAME_OFFSET_INVALID;
217     }
218
219     /* Calculate page access protection flags */
220     // Get frame cap rights
221     paging_x86_32_flags_t flags =
222         paging_x86_32_cap_to_page_flags(src->rights);
223     // Mask with provided access rights mask
224     flags = paging_x86_32_mask_attrs(flags, X86_32_PTABLE_ACCESS(uflags));
225     // Add additional arch-specific flags
226     flags |= X86_32_PTABLE_FLAGS(uflags);
227     // Unconditionally mark the page present
228     flags |= X86_32_PTABLE_PRESENT;
229
230     // Convert destination base address
231     genpaddr_t dest_gp   = get_address(dest);
232     lpaddr_t dest_lp     = gen_phys_to_local_phys(dest_gp);
233     lvaddr_t dest_lv     = local_phys_to_mem(dest_lp);
234     // Convert source base address
235     genpaddr_t src_gp   = get_address(src);
236     lpaddr_t src_lp     = gen_phys_to_local_phys(src_gp);
237     // Set metadata
238     struct cte *src_cte = cte_for_cap(src);
239     src_cte->mapping_info.pte = dest_lp + slot * sizeof(union x86_32_ptable_entry);
240     src_cte->mapping_info.pte_count = pte_count;
241     src_cte->mapping_info.offset = offset;
242
243
244     for (; slot < last_slot; slot++, offset += X86_32_BASE_PAGE_SIZE) {
245         union x86_32_ptable_entry *entry =
246             (union x86_32_ptable_entry *)dest_lv + slot;
247
248         /* FIXME: Flush TLB if the page is already present
249          * in the meantime, since we don't do this, we just assert that
250          * we never reuse a VA mapping */
251         if (X86_32_IS_PRESENT(entry)) {
252             panic("Trying to map into an already present page NYI.");
253         }
254
255         // Carry out the page mapping
256         paging_x86_32_map(entry, src_lp + offset, flags);
257     }
258
259     return SYS_ERR_OK;
260 }
261
262 typedef errval_t (*mapping_handler_t)(struct capability *dest_cap,
263                                       cslot_t dest_slot,
264                                       struct capability *src_cap,
265                                       uintptr_t flags, uintptr_t offset,
266                                       uintptr_t pte_count);
267
268 /// Dispatcher table for the type of mapping to create
269 static mapping_handler_t handler[ObjType_Num] = {
270 #ifdef CONFIG_PAE
271     [ObjType_VNode_x86_32_pdpt]   = x86_32_pdpt,
272 #endif
273     [ObjType_VNode_x86_32_pdir]   = x86_32_pdir,
274     [ObjType_VNode_x86_32_ptable] = x86_32_ptable,
275 };
276
277 #define DIAGNOSTIC_ON_ERROR 1
278 #define RETURN_ON_ERROR 1
279
280 /// Create page mappings
281 errval_t caps_copy_to_vnode(struct cte *dest_vnode_cte, cslot_t dest_slot,
282                             struct cte *src_cte, uintptr_t flags,
283                             uintptr_t offset, uintptr_t pte_count)
284 {
285     assert(type_is_vnode(dest_vnode_cte->cap.type));
286
287     struct capability *src_cap  = &src_cte->cap;
288     struct capability *dest_cap = &dest_vnode_cte->cap;
289     mapping_handler_t handler_func = handler[dest_cap->type];
290
291     assert(handler_func != NULL);
292
293     if (src_cte->mapping_info.pte) {
294         // already mapped
295 #if DIAGNOSTIC_ON_ERROR
296         printf("caps_copy_to_vnode: this copy is already mapped @0x%lx\n", src_cte->mapping_info.pte);
297 #endif
298 #if RETURN_ON_ERROR
299         return SYS_ERR_VM_ALREADY_MAPPED;
300 #endif
301     }
302
303     cslot_t last_slot = dest_slot + pte_count;
304
305     // TODO: PAE
306     if (last_slot > X86_32_PTABLE_SIZE) {
307         // requested map overlaps leaf page table
308 #if DIAGNOSTIC_ON_ERROR
309         printf("caps_copy_to_vnode: requested mapping spans multiple leaf page tables\n");
310 #endif
311 #if RETURN_ON_ERROR
312         return SYS_ERR_VM_RETRY_SINGLE;
313 #endif
314     }
315
316 #if 0
317     genvaddr_t vaddr;
318     compile_vaddr(dest_vnode_cte, dest_slot, &vaddr);
319     printf("caps_copy_to_vnode: mapping %lu pages (slots %"PRIuCSLOT" to %"PRIuCSLOT") to 0x%"PRIxGENVADDR"\n",
320             pte_count, dest_slot, last_slot, vaddr);
321     genpaddr_t paddr = get_address(&src_cte->cap) + offset;
322     printf("mapping 0x%"PRIxGENPADDR" to 0x%"PRIxGENVADDR"\n", paddr, vaddr);
323 #endif
324
325
326     errval_t r = handler_func(dest_cap, dest_slot, src_cap, flags, offset, pte_count);
327     if (err_is_fail(r)) {
328         printf("caps_copy_to_vnode: handler func returned %ld\n", r);
329     }
330 #if 0
331     else {
332         printf("mapping_info.pte       = 0x%lx\n", src_cte->mapping_info.pte);
333         printf("mapping_info.offset    = 0x%"PRIx64"\n", src_cte->mapping_info.offset);
334         printf("mapping_info.pte_count = %zu\n", src_cte->mapping_info.pte_count);
335     }
336 #endif
337     return r;
338 }
339
340 size_t do_unmap(lvaddr_t pt, cslot_t slot, size_t num_pages)
341 {
342     size_t unmapped_pages = 0;
343     union x86_32_ptable_entry *ptentry = (union x86_32_ptable_entry *)pt + slot;
344     for (int i = 0; i < num_pages; i++) {
345         ptentry++->raw = 0;
346         unmapped_pages++;
347     }
348     return unmapped_pages;
349 }
350
351 static inline void read_pt_entry(struct capability *pgtable, size_t slot,
352         genpaddr_t *mapped_addr, lpaddr_t *pte,
353         void **entry)
354 {
355     assert(type_is_vnode(pgtable->type));
356
357     genpaddr_t paddr;
358     lpaddr_t pte_;
359     void *entry_;
360
361     genpaddr_t gp = get_address(pgtable);
362     lpaddr_t lp = gen_phys_to_local_phys(gp);
363     lvaddr_t lv = local_phys_to_mem(lp);
364
365     // get paddr
366     switch (pgtable->type) {
367         case ObjType_VNode_x86_32_pdpt:
368         case ObjType_VNode_x86_32_pdir: {
369             union x86_32_pdir_entry *e =
370                 (union x86_32_pdir_entry *)lv + slot;
371             paddr = e->d.base_addr << BASE_PAGE_BITS;
372             entry_ = e;
373             pte_ = lp + slot * sizeof(union x86_32_pdir_entry);
374             break;
375         }
376         case ObjType_VNode_x86_32_ptable: {
377             union x86_32_ptable_entry *e =
378                 (union x86_32_ptable_entry *)lv + slot;
379             paddr = e->base.base_addr << BASE_PAGE_BITS;
380             entry_ = e;
381             pte_ = lp + slot * sizeof(union x86_32_ptable_entry);
382             break;
383         }
384         default:
385             assert(!"Should not get here");
386     }
387
388     if (mapped_addr) {
389         *mapped_addr = paddr;
390     }
391     if (pte) {
392         *pte = pte_;
393     }
394     if (entry) {
395         *entry = entry_;
396     }
397 }
398
399 errval_t page_mappings_unmap(struct capability *pgtable, struct cte *mapping, size_t slot, size_t num_pages)
400 {
401     assert(type_is_vnode(pgtable->type));
402     //printf("page_mappings_unmap(%zd pages, slot = %zd)\n", num_pages, slot);
403
404     // get page table entry data
405     genpaddr_t paddr;
406     //lpaddr_t pte;
407     read_pt_entry(pgtable, slot, &paddr, NULL, NULL);
408     lvaddr_t pt = local_phys_to_mem(gen_phys_to_local_phys(get_address(pgtable)));
409
410     // get virtual address of first page
411     // TODO: error checking
412     genvaddr_t vaddr;
413     struct cte *leaf_pt = cte_for_cap(pgtable);
414     compile_vaddr(leaf_pt, slot, &vaddr);
415     // printf("vaddr = 0x%"PRIxGENVADDR"\n", vaddr);
416     // printf("num_pages = %zu\n", num_pages);
417
418     // get cap for mapping
419     /*
420     struct cte *mem;
421     errval_t err = lookup_cap_for_mapping(paddr, pte, &mem);
422     if (err_is_fail(err)) {
423         printf("page_mappings_unmap: %ld\n", err);
424         return err;
425     }
426     */
427     //printf("state before unmap: mapped_pages = %zd\n", mem->mapping_info.mapped_pages);
428     //printf("state before unmap: num_pages    = %zd\n", num_pages);
429
430     if (num_pages != mapping->mapping_info.pte_count) {
431         // want to unmap a different amount of pages than was mapped
432         return SYS_ERR_VM_MAP_SIZE;
433     }
434
435     do_unmap(pt, slot, num_pages);
436
437     // flush TLB for unmapped pages
438     // TODO: heuristic that decides if selective or full flush is more
439     //       efficient?
440     if (num_pages > 1) {
441         do_full_tlb_flush();
442     } else {
443         do_one_tlb_flush(vaddr);
444     }
445
446     // update mapping info
447     memset(&mapping->mapping_info, 0, sizeof(struct mapping_info));
448
449     return SYS_ERR_OK;
450 }
451
452 errval_t page_mappings_modify_flags(struct capability *frame, size_t offset,
453                                     size_t pages, size_t uflags)
454 {
455     struct cte *mapping = cte_for_cap(frame);
456     struct mapping_info *info = &mapping->mapping_info;
457
458     /* Calculate page access protection flags */
459     // Get frame cap rights
460     paging_x86_32_flags_t flags =
461         paging_x86_32_cap_to_page_flags(frame->rights);
462     // Mask with provided access rights mask
463     flags = paging_x86_32_mask_attrs(flags, X86_32_PTABLE_ACCESS(uflags));
464     // Add additional arch-specific flags
465     flags |= X86_32_PTABLE_FLAGS(uflags);
466     // Unconditionally mark the page present
467     flags |= X86_32_PTABLE_PRESENT;
468
469     /* Calculate location of page table entries we need to modify */
470     lvaddr_t base = local_phys_to_mem(info->pte) + offset;
471
472     for (int i = 0; i < pages; i++) {
473         union x86_32_ptable_entry *entry =
474             (union x86_32_ptable_entry *)base + i;
475         paging_x86_32_modify_flags(entry, flags);
476     }
477
478     return paging_tlb_flush_range(mapping, pages);
479 }
480
481 void paging_dump_tables(struct dcb *dispatcher)
482 {
483     printf("dump_hw_page_tables\n");
484     lvaddr_t root_pt = local_phys_to_mem(dispatcher->vspace);
485
486 #ifdef CONFIG_PAE
487     // loop over pdpt entries
488     for (int pdir_index = 0; pdir_index < X86_64_PDPTE_SIZE; pdir_index++) {
489         // get pdir
490         union x86_32_pdpte_entry *pdir = (union x86_64_pdir_entry *)root_pt + pdir_index;
491         if (!pdir->raw) { continue; }
492         genpaddr_t pdir_gp = pdir->d.base_addr << BASE_PAGE_BITS;
493         lvaddr_t pdir_lv = local_phys_to_mem(gen_phys_to_local_phys(pdir_gp));
494 #else
495         int pdir_index = 0;
496         lvaddr_t pdir_lv = root_pt;
497 #endif
498
499         for (int ptable_index = 0; ptable_index < X86_32_PDIR_SIZE; ptable_index++) {
500             // get ptable
501             union x86_32_pdir_entry *ptable = (union x86_32_pdir_entry *)pdir_lv + ptable_index;
502             if (!ptable->raw) { continue; }
503             genpaddr_t ptable_gp = ptable->d.base_addr << BASE_PAGE_BITS;
504             lvaddr_t ptable_lv = local_phys_to_mem(gen_phys_to_local_phys(ptable_gp));
505
506             for (int entry = 0; entry < X86_32_PTABLE_SIZE; entry++) {
507                 union x86_32_ptable_entry *e =
508                     (union x86_32_ptable_entry *)ptable_lv + entry;
509                 genpaddr_t paddr = (genpaddr_t)e->base.base_addr << BASE_PAGE_BITS;
510                 if (!paddr) {
511                     continue;
512                 }
513                 printf("%d.%d.%d: 0x%"PRIxGENPADDR"\n", pdir_index, ptable_index, entry, paddr);
514             }
515         }
516 #ifdef CONFIG_PAE
517     } // endfor PDPT entries
518 #endif
519 }