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