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