7 * Copyright (c) 2010-2013 ETH Zurich.
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.
17 #include <target/x86_32/paging_kernel_target.h>
18 #include <target/x86_32/offsets_target.h>
19 #include <paging_kernel_arch.h>
21 #include <cap_predicates.h>
23 static inline struct cte *cte_for_cap(struct capability *cap)
25 return (struct cte *) (cap - offsetof(struct cte, cap));
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)
34 if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
35 return SYS_ERR_VNODE_SLOT_INVALID;
38 if (pte_count > 1) { // disallow multiple pdpt mappings at a time
39 return SYS_ERR_VM_MAP_SIZE;
42 if (src->type != ObjType_VNode_x86_32_pdir) { // Right mapping
43 return SYS_ERR_WRONG_MAPPING;
46 if(slot >= X86_32_PDPTE_BASE(X86_32_MEMORY_OFFSET)) { // Kernel mapped here
47 return SYS_ERR_VNODE_SLOT_RESERVED;
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;
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;
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
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)
78 //printf("x86_32_pdir\n");
79 if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
80 return SYS_ERR_VNODE_SLOT_INVALID;
83 if (slot + pte_count > X86_32_PTABLE_SIZE) { // disallow more than one page at a time
84 return SYS_ERR_VM_MAP_SIZE;
88 if(slot >= X86_32_PDIR_BASE(X86_32_MEMORY_OFFSET)) { // Kernel mapped here
89 return SYS_ERR_VNODE_SLOT_RESERVED;
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)
98 printf("\tlarge page\n");
99 cslot_t last_slot = slot + pte_count;
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;
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;
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;
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);
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;
141 printf("start looping\n");
142 for (; slot < last_slot; slot++, offset += page_size) {
144 union x86_32_ptable_entry *entry =
145 (union x86_32_ptable_entry *)dest_lv + slot;
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;
156 // Carry out the page mapping
157 paging_x86_32_map_large(entry, src_lp + offset, flags_large);
160 printf("mapping complete\n");
164 if (src->type != ObjType_VNode_x86_32_ptable) { // Right mapping
166 return SYS_ERR_WRONG_MAPPING;
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;
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;
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);
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)
197 //printf("x86_32_ptable\n");
198 if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
199 return SYS_ERR_VNODE_SLOT_INVALID;
202 cslot_t last_slot = slot + pte_count;
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;
209 if (src->type != ObjType_Frame &&
210 src->type != ObjType_DevFrame) { // Right mapping
211 return SYS_ERR_WRONG_MAPPING;
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;
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;
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);
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;
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;
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.");
255 // Carry out the page mapping
256 paging_x86_32_map(entry, src_lp + offset, flags);
262 typedef errval_t (*mapping_handler_t)(struct capability *dest_cap,
264 struct capability *src_cap,
265 uintptr_t flags, uintptr_t offset,
266 uintptr_t pte_count);
268 /// Dispatcher table for the type of mapping to create
269 static mapping_handler_t handler[ObjType_Num] = {
271 [ObjType_VNode_x86_32_pdpt] = x86_32_pdpt,
273 [ObjType_VNode_x86_32_pdir] = x86_32_pdir,
274 [ObjType_VNode_x86_32_ptable] = x86_32_ptable,
277 #define DIAGNOSTIC_ON_ERROR 1
278 #define RETURN_ON_ERROR 1
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)
285 assert(type_is_vnode(dest_vnode_cte->cap.type));
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];
291 assert(handler_func != NULL);
293 if (src_cte->mapping_info.pte) {
295 #if DIAGNOSTIC_ON_ERROR
296 printf("caps_copy_to_vnode: this copy is already mapped @0x%lx\n", src_cte->mapping_info.pte);
299 return SYS_ERR_VM_ALREADY_MAPPED;
303 cslot_t last_slot = dest_slot + pte_count;
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");
312 return SYS_ERR_VM_RETRY_SINGLE;
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);
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);
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);
340 size_t do_unmap(lvaddr_t pt, cslot_t slot, size_t num_pages)
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++) {
348 return unmapped_pages;
351 static inline void read_pt_entry(struct capability *pgtable, size_t slot,
352 genpaddr_t *mapped_addr, lpaddr_t *pte,
355 assert(type_is_vnode(pgtable->type));
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);
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;
373 pte_ = lp + slot * sizeof(union x86_32_pdir_entry);
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;
381 pte_ = lp + slot * sizeof(union x86_32_ptable_entry);
385 assert(!"Should not get here");
389 *mapped_addr = paddr;
399 errval_t page_mappings_unmap(struct capability *pgtable, struct cte *mapping, size_t slot, size_t num_pages)
401 assert(type_is_vnode(pgtable->type));
402 //printf("page_mappings_unmap(%zd pages, slot = %zd)\n", num_pages, slot);
404 // get page table entry data
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)));
410 // get virtual address of first page
411 // TODO: error checking
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);
418 // get cap for mapping
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);
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);
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;
435 do_unmap(pt, slot, num_pages);
437 // flush TLB for unmapped pages
438 // TODO: heuristic that decides if selective or full flush is more
443 do_one_tlb_flush(vaddr);
446 // update mapping info
447 memset(&mapping->mapping_info, 0, sizeof(struct mapping_info));
452 errval_t page_mappings_modify_flags(struct capability *frame, size_t offset,
453 size_t pages, size_t uflags)
455 struct cte *mapping = cte_for_cap(frame);
456 struct mapping_info *info = &mapping->mapping_info;
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;
469 /* Calculate location of page table entries we need to modify */
470 lvaddr_t base = local_phys_to_mem(info->pte) + offset;
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);
478 return paging_tlb_flush_range(mapping, pages);
481 void paging_dump_tables(struct dcb *dispatcher)
483 printf("dump_hw_page_tables\n");
484 lvaddr_t root_pt = local_phys_to_mem(dispatcher->vspace);
487 // loop over pdpt entries
488 for (int pdir_index = 0; pdir_index < X86_64_PDPTE_SIZE; pdir_index++) {
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));
496 lvaddr_t pdir_lv = root_pt;
499 for (int ptable_index = 0; ptable_index < X86_32_PDIR_SIZE; ptable_index++) {
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));
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;
513 printf("%d.%d.%d: 0x%"PRIxGENPADDR"\n", pdir_index, ptable_index, entry, paddr);
517 } // endfor PDPT entries