7 * Copyright (c) 2010-2013 ETH Zurich.
8 * Copyright (c) 2014, HP Labs.
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.
18 #include <target/x86_32/paging_kernel_target.h>
19 #include <target/x86_32/offsets_target.h>
20 #include <paging_kernel_arch.h>
22 #include <cap_predicates.h>
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)
30 if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
31 return SYS_ERR_VNODE_SLOT_INVALID;
34 if (pte_count > 1) { // disallow multiple pdpt mappings at a time
35 return SYS_ERR_VM_MAP_SIZE;
38 if (src->type != ObjType_VNode_x86_32_pdir) { // Right mapping
39 return SYS_ERR_WRONG_MAPPING;
42 if(slot >= X86_32_PDPTE_BASE(X86_32_MEMORY_OFFSET)) { // Kernel mapped here
43 return SYS_ERR_VNODE_SLOT_RESERVED;
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;
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;
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
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)
74 //printf("x86_32_pdir\n");
75 if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
76 return SYS_ERR_VNODE_SLOT_INVALID;
79 if (slot + pte_count > X86_32_PTABLE_SIZE) {
80 // check that mapping fits page directory
81 return SYS_ERR_VM_MAP_SIZE;
85 if(slot >= X86_32_PDIR_BASE(X86_32_MEMORY_OFFSET)) { // Kernel mapped here
86 return SYS_ERR_VNODE_SLOT_RESERVED;
91 if(src->type == ObjType_Frame || src->type == ObjType_DevFrame)
93 cslot_t last_slot = slot + pte_count;
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;
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;
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);
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;
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;
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;
136 // Carry out the page mapping
137 paging_x86_32_map_large(entry, src_lp + offset, flags_large);
143 if (src->type != ObjType_VNode_x86_32_ptable) { // Right mapping
144 return SYS_ERR_WRONG_MAPPING;
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;
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;
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);
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)
175 //printf("x86_32_ptable\n");
176 if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table
177 return SYS_ERR_VNODE_SLOT_INVALID;
180 cslot_t last_slot = slot + pte_count;
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;
187 if (src->type != ObjType_Frame &&
188 src->type != ObjType_DevFrame) { // Right mapping
189 return SYS_ERR_WRONG_MAPPING;
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;
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;
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);
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;
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;
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.");
233 // Carry out the page mapping
234 paging_x86_32_map(entry, src_lp + offset, flags);
240 typedef errval_t (*mapping_handler_t)(struct capability *dest_cap,
242 struct capability *src_cap,
243 uintptr_t flags, uintptr_t offset,
244 uintptr_t pte_count);
246 /// Dispatcher table for the type of mapping to create
247 static mapping_handler_t handler[ObjType_Num] = {
249 [ObjType_VNode_x86_32_pdpt] = x86_32_pdpt,
251 [ObjType_VNode_x86_32_pdir] = x86_32_pdir,
252 [ObjType_VNode_x86_32_ptable] = x86_32_ptable,
255 #define DIAGNOSTIC_ON_ERROR 1
256 #define RETURN_ON_ERROR 1
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)
263 assert(type_is_vnode(dest_vnode_cte->cap.type));
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];
269 assert(handler_func != NULL);
271 if (src_cte->mapping_info.pte) {
273 #if DIAGNOSTIC_ON_ERROR
274 printf("caps_copy_to_vnode: this copy is already mapped @0x%lx\n", src_cte->mapping_info.pte);
277 return SYS_ERR_VM_ALREADY_MAPPED;
281 cslot_t last_slot = dest_slot + pte_count;
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");
290 return SYS_ERR_VM_RETRY_SINGLE;
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);
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);
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);
318 size_t do_unmap(lvaddr_t pt, cslot_t slot, size_t num_pages)
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++) {
326 return unmapped_pages;
329 static inline void read_pt_entry(struct capability *pgtable, size_t slot,
330 genpaddr_t *mapped_addr, lpaddr_t *pte,
333 assert(type_is_vnode(pgtable->type));
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);
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;
351 pte_ = lp + slot * sizeof(union x86_32_pdir_entry);
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;
359 pte_ = lp + slot * sizeof(union x86_32_ptable_entry);
363 assert(!"Should not get here");
367 *mapped_addr = paddr;
377 errval_t page_mappings_unmap(struct capability *pgtable, struct cte *mapping, size_t slot, size_t num_pages)
379 assert(type_is_vnode(pgtable->type));
380 //printf("page_mappings_unmap(%zd pages, slot = %zd)\n", num_pages, slot);
382 // get page table entry data
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)));
388 // get virtual address of first page
389 // TODO: error checking
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);
396 // get cap for mapping
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);
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);
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;
413 do_unmap(pt, slot, num_pages);
415 // flush TLB for unmapped pages
416 // TODO: heuristic that decides if selective or full flush is more
418 // currently set to trivially flush entire tlb to make large page unmapping work
419 if (num_pages > 1 || true) {
422 do_one_tlb_flush(vaddr);
425 // update mapping info
426 memset(&mapping->mapping_info, 0, sizeof(struct mapping_info));
431 errval_t page_mappings_modify_flags(struct capability *frame, size_t offset,
432 size_t pages, size_t uflags)
434 struct cte *mapping = cte_for_cap(frame);
435 struct mapping_info *info = &mapping->mapping_info;
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;
448 /* Calculate location of page table entries we need to modify */
449 lvaddr_t base = local_phys_to_mem(info->pte) + offset;
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);
457 return paging_tlb_flush_range(mapping, pages);
460 void paging_dump_tables(struct dcb *dispatcher)
462 printf("dump_hw_page_tables\n");
463 lvaddr_t root_pt = local_phys_to_mem(dispatcher->vspace);
466 // loop over pdpt entries
467 for (int pdir_index = 0; pdir_index < X86_32_PDPTE_SIZE; pdir_index++) {
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));
475 lvaddr_t pdir_lv = root_pt;
478 // only go to 512 because upper half of address space is kernel space
480 // TODO: figure out what we need to do here for PAE
481 for (int ptable_index = 0; ptable_index < 512; ptable_index++) {
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) {
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);
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));
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;
502 printf("%d.%d.%d: 0x%"PRIxGENPADDR"\n", pdir_index, ptable_index, entry, paddr);
506 } // endfor PDPT entries