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