1/** 2 * \file 3 * \brief 4 */ 5 6/* 7 * Copyright (c) 2010-2013, 2016 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 <mdb/mdb_tree.h> 22#include <string.h> 23#include <cap_predicates.h> 24#include <paging_generic.h> 25 26#ifdef CONFIG_PAE 27/// Map within a x86_32 pdpt 28static errval_t x86_32_pdpt(struct capability *dest, cslot_t slot, 29 struct capability * src, uintptr_t flags, 30 uintptr_t offset, uintptr_t pte_count, 31 struct cte *mapping_cte) 32{ 33 if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table 34 return SYS_ERR_VNODE_SLOT_INVALID; 35 } 36 37 if (pte_count > 1) { // disallow multiple pdpt mappings at a time 38 return SYS_ERR_VM_MAP_SIZE; 39 } 40 41 if (src->type != ObjType_VNode_x86_32_pdir) { // Right mapping 42 return SYS_ERR_WRONG_MAPPING; 43 } 44 45 if(slot >= X86_32_PDPTE_BASE(X86_32_MEMORY_OFFSET)) { // Kernel mapped here 46 return SYS_ERR_VNODE_SLOT_RESERVED; 47 } 48 49 // Destination 50 genpaddr_t dest_gp = dest->u.vnode_x86_32_pdpt.base; 51 lpaddr_t dest_lp = gen_phys_to_local_phys(dest_gp); 52 lvaddr_t dest_lv = local_phys_to_mem(dest_lp); 53 union x86_32_pdpte_entry *entry = 54 (union x86_32_pdpte_entry *)dest_lv + slot; 55 56 // Set metadata 57 create_mapping_cap(mapping_cte, src, 58 dest_lp + slot * sizeof(union x86_32_pdpte_entry), 59 pte_count); 60 61 // Source 62 genpaddr_t src_gp = src->u.vnode_x86_32_pdir.base; 63 lpaddr_t src_lp = gen_phys_to_local_phys(src_gp); 64 paging_x86_32_map_pdpte(entry, src_lp); 65 paging_x86_32_context_switch(dcb_current->vspace); // To flush TLB 66 67 return SYS_ERR_OK; 68} 69#endif 70 71/// Map within a x86_32 pdir 72static errval_t x86_32_pdir(struct capability *dest, cslot_t slot, 73 struct capability * src, uintptr_t flags, 74 uintptr_t offset, uintptr_t pte_count, 75 struct cte *mapping_cte) 76{ 77 //printf("x86_32_pdir\n"); 78 if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table 79 return SYS_ERR_VNODE_SLOT_INVALID; 80 } 81 82 if (slot + pte_count > X86_32_PTABLE_SIZE) { 83 // check that mapping fits page directory 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 if(src->type == ObjType_Frame || src->type == ObjType_DevFrame) 95 { 96 cslot_t last_slot = slot + pte_count; 97 98 // check offset within frame 99 if (offset + pte_count * X86_32_LARGE_PAGE_SIZE > get_size(src)) { 100 return SYS_ERR_FRAME_OFFSET_INVALID; 101 } 102 103 /* Calculate page access protection flags */ 104 // Get frame cap rights 105 paging_x86_32_flags_t flags_large = 106 paging_x86_32_cap_to_page_flags(src->rights); 107 // Mask with provided access rights mask 108 flags_large = paging_x86_32_mask_attrs(flags_large, X86_32_PTABLE_ACCESS(flags)); 109 // Add additional arch-specific flags 110 flags_large |= X86_32_PTABLE_FLAGS(flags); 111 // Unconditionally mark the page present 112 flags_large |= X86_32_PTABLE_PRESENT; 113 114 // Convert destination base address 115 genpaddr_t dest_gp = get_address(dest); 116 lpaddr_t dest_lp = gen_phys_to_local_phys(dest_gp); 117 lvaddr_t dest_lv = local_phys_to_mem(dest_lp); 118 // Convert source base address 119 genpaddr_t src_gp = get_address(src); 120 lpaddr_t src_lp = gen_phys_to_local_phys(src_gp); 121 // Set metadata 122 create_mapping_cap(mapping_cte, src, 123 dest_lp + slot * sizeof(union x86_32_ptable_entry), 124 offset, 125 pte_count); 126 127 for (; slot < last_slot; slot++, offset += X86_32_LARGE_PAGE_SIZE) { 128 union x86_32_ptable_entry *entry = 129 (union x86_32_ptable_entry *)dest_lv + slot; 130 131 /* FIXME: Flush TLB if the page is already present 132 * in the meantime, since we don't do this, we just assert that 133 * we never reuse a VA mapping */ 134 if (X86_32_IS_PRESENT(entry)) { 135 printf("Trying to map into an already present page NYI.\n"); 136 return SYS_ERR_VNODE_SLOT_INUSE; 137 } 138 139 // Carry out the page mapping 140 paging_x86_32_map_large(entry, src_lp + offset, flags_large); 141 } 142 143 return SYS_ERR_OK; 144 } 145 146 if (src->type != ObjType_VNode_x86_32_ptable) { // Right mapping 147 return SYS_ERR_WRONG_MAPPING; 148 } 149 150 // Destination 151 genpaddr_t dest_gp = dest->u.vnode_x86_32_pdir.base; 152 lpaddr_t dest_lp = gen_phys_to_local_phys(dest_gp); 153 lvaddr_t dest_lv = local_phys_to_mem(dest_lp); 154 union x86_32_pdir_entry *entry = 155 (union x86_32_pdir_entry *)dest_lv + slot; 156 157 // Set metadata 158 create_mapping_cap(mapping_cte, src, 159 dest_lp + slot * sizeof(union x86_32_pdir_entry), 160 pte_count); 161 162 163 // Source 164 // XXX: offset is ignored 165 genpaddr_t src_gp = src->u.vnode_x86_32_pdir.base; 166 lpaddr_t src_lp = gen_phys_to_local_phys(src_gp); 167 paging_x86_32_map_table(entry, src_lp); 168 169 return SYS_ERR_OK; 170} 171 172/// Map within a x86_32 ptable 173static errval_t x86_32_ptable(struct capability *dest, cslot_t slot, 174 struct capability * src, uintptr_t uflags, 175 uintptr_t offset, uintptr_t pte_count, 176 struct cte *mapping_cte) 177{ 178 //printf("x86_32_ptable\n"); 179 if (slot >= X86_32_PTABLE_SIZE) { // Slot within page table 180 return SYS_ERR_VNODE_SLOT_INVALID; 181 } 182 183 cslot_t last_slot = slot + pte_count; 184 185 if (last_slot > X86_32_PTABLE_SIZE) { 186 printf("slot = %"PRIuCSLOT", last_slot = %"PRIuCSLOT", PTABLE_SIZE = %d\n", slot, last_slot, X86_32_PTABLE_SIZE); 187 return SYS_ERR_VM_MAP_SIZE; 188 } 189 190 if (src->type != ObjType_Frame && 191 src->type != ObjType_DevFrame) { // Right mapping 192 return SYS_ERR_WRONG_MAPPING; 193 } 194 195 // check offset within frame 196 if (offset + pte_count * X86_32_BASE_PAGE_SIZE > get_size(src)) { 197 return SYS_ERR_FRAME_OFFSET_INVALID; 198 } 199 200 /* Calculate page access protection flags */ 201 // Get frame cap rights 202 paging_x86_32_flags_t flags = 203 paging_x86_32_cap_to_page_flags(src->rights); 204 // Mask with provided access rights mask 205 flags = paging_x86_32_mask_attrs(flags, X86_32_PTABLE_ACCESS(uflags)); 206 // Add additional arch-specific flags 207 flags |= X86_32_PTABLE_FLAGS(uflags); 208 // Unconditionally mark the page present 209 flags |= X86_32_PTABLE_PRESENT; 210 211 // Convert destination base address 212 genpaddr_t dest_gp = get_address(dest); 213 lpaddr_t dest_lp = gen_phys_to_local_phys(dest_gp); 214 lvaddr_t dest_lv = local_phys_to_mem(dest_lp); 215 // Convert source base address 216 genpaddr_t src_gp = get_address(src); 217 lpaddr_t src_lp = gen_phys_to_local_phys(src_gp); 218 // Set metadata 219 create_mapping_cap(mapping_cte, src, 220 dest_lp + slot * sizeof(union x86_32_ptable_entry), 221 offset, 222 pte_count); 223 224 225 for (; slot < last_slot; slot++, offset += X86_32_BASE_PAGE_SIZE) { 226 union x86_32_ptable_entry *entry = 227 (union x86_32_ptable_entry *)dest_lv + slot; 228 229 /* FIXME: Flush TLB if the page is already present 230 * in the meantime, since we don't do this, we just assert that 231 * we never reuse a VA mapping */ 232 if (X86_32_IS_PRESENT(entry)) { 233 panic("Trying to map into an already present page NYI."); 234 } 235 236 // Carry out the page mapping 237 paging_x86_32_map(entry, src_lp + offset, flags); 238 } 239 240 return SYS_ERR_OK; 241} 242 243typedef errval_t (*mapping_handler_t)(struct capability *dest_cap, 244 cslot_t dest_slot, 245 struct capability *src_cap, 246 uintptr_t flags, uintptr_t offset, 247 uintptr_t pte_count, 248 struct cte *mapping_cte); 249 250/// Dispatcher table for the type of mapping to create 251static mapping_handler_t handler[ObjType_Num] = { 252#ifdef CONFIG_PAE 253 [ObjType_VNode_x86_32_pdpt] = x86_32_pdpt, 254#endif 255 [ObjType_VNode_x86_32_pdir] = x86_32_pdir, 256 [ObjType_VNode_x86_32_ptable] = x86_32_ptable, 257}; 258 259/// Create page mappings 260errval_t caps_copy_to_vnode(struct cte *dest_vnode_cte, cslot_t dest_slot, 261 struct cte *src_cte, uintptr_t flags, 262 uintptr_t offset, uintptr_t pte_count, 263 struct cte *mapping_cte) 264{ 265 assert(type_is_vnode(dest_vnode_cte->cap.type)); 266 assert(mapping_cte->cap.type == ObjType_Null); 267 268 struct capability *src_cap = &src_cte->cap; 269 struct capability *dest_cap = &dest_vnode_cte->cap; 270 mapping_handler_t handler_func = handler[dest_cap->type]; 271 272 assert(handler_func != NULL); 273 274 cslot_t last_slot = dest_slot + pte_count; 275 276 // TODO: PAE 277 if (last_slot > X86_32_PTABLE_SIZE) { 278 // requested map overlaps leaf page table 279 debug(SUBSYS_CAPS, 280 "caps_copy_to_vnode: requested mapping spans multiple leaf page tables\n"); 281 return SYS_ERR_VM_RETRY_SINGLE; 282 } 283 284 errval_t r = handler_func(dest_cap, dest_slot, src_cap, flags, offset, 285 pte_count, mapping_cte); 286 if (err_is_fail(r)) { 287 assert(mapping_cte->cap.type == ObjType_Null); 288 debug(SUBSYS_PAGING, "caps_copy_to_vnode: handler func returned %d\n", r); 289 return r; 290 } 291 292 /* insert mapping cap into mdb */ 293 errval_t err = mdb_insert(mapping_cte); 294 if (err_is_fail(err)) { 295 printk(LOG_ERR, "%s: mdb_insert: %"PRIuERRV"\n", __FUNCTION__, err); 296 } 297 298 TRACE_CAP_MSG("created", mapping_cte); 299 300 return err; 301} 302 303size_t do_unmap(lvaddr_t pt, cslot_t slot, size_t num_pages) 304{ 305 size_t unmapped_pages = 0; 306 union x86_32_ptable_entry *ptentry = (union x86_32_ptable_entry *)pt + slot; 307 for (int i = 0; i < num_pages; i++) { 308 ptentry++->raw = 0; 309 unmapped_pages++; 310 } 311 return unmapped_pages; 312} 313 314errval_t page_mappings_modify_flags(struct capability *mapping, size_t offset, 315 size_t pages, size_t mflags, genvaddr_t va_hint) 316{ 317 assert(type_is_mapping(mapping->type)); 318 struct Frame_Mapping *info = &mapping->u.frame_mapping; 319 320 /* Calculate page access protection flags */ 321 // Get frame cap rights 322 paging_x86_32_flags_t flags = 323 paging_x86_32_cap_to_page_flags(info->cap->rights); 324 // Mask with provided access rights mask 325 flags = paging_x86_32_mask_attrs(flags, X86_32_PTABLE_ACCESS(mflags)); 326 // Add additional arch-specific flags 327 flags |= X86_32_PTABLE_FLAGS(mflags); 328 // Unconditionally mark the page present 329 flags |= X86_32_PTABLE_PRESENT; 330 331 // check arguments 332 if (offset >= X86_32_PTABLE_SIZE) { // Within pagetable 333 return SYS_ERR_VNODE_SLOT_INVALID; 334 } 335 if (offset + pages > X86_32_PTABLE_SIZE) { // mapping size ok 336 return SYS_ERR_VM_MAP_SIZE; 337 } 338 339 /* Calculate location of page table entries we need to modify */ 340 lvaddr_t base = local_phys_to_mem(info->pte) + 341 offset * sizeof(union x86_32_ptable_entry); 342 343 // get pt cap to figure out page size 344 struct cte *leaf_pt; 345 errval_t err; 346 err = mdb_find_cap_for_address(info->pte, &leaf_pt); 347 if (err_is_fail(err)) { 348 return err; 349 } 350 351 size_t pagesize = BASE_PAGE_SIZE; 352 switch(leaf_pt->cap.type) { 353 case ObjType_VNode_x86_32_ptable : 354 for (int i = 0; i < pages; i++) { 355 union x86_32_ptable_entry *entry = 356 (union x86_32_ptable_entry *)base + i; 357 paging_x86_32_modify_flags(entry, flags); 358 } 359 break; 360 case ObjType_VNode_x86_32_pdir : 361 for (int i = 0; i < pages; i++) { 362 union x86_32_ptable_entry *entry = 363 (union x86_32_ptable_entry *)base + i; 364 paging_x86_32_modify_flags_large(entry, flags); 365 } 366 pagesize = LARGE_PAGE_SIZE; 367 break; 368 default: 369 return SYS_ERR_WRONG_MAPPING; 370 } 371 372 if (va_hint != 0 && va_hint > BASE_PAGE_SIZE) { 373 // use as direct hint 374 // invlpg should work for large/huge pages 375 for (int i = 0; i < pages; i++) { 376 do_one_tlb_flush(va_hint + i * pagesize); 377 } 378 } else { 379 /* do full TLB flush */ 380 do_full_tlb_flush(); 381 } 382 return SYS_ERR_OK; 383} 384 385void paging_dump_tables(struct dcb *dispatcher) 386{ 387 if (dispatcher->vspace > X86_32_PADDR_SPACE_LIMIT) { 388 printk(LOG_ERR, "dispatcher->vspace = 0x%"PRIxLPADDR": too high!\n" , 389 dispatcher->vspace); 390 return; 391 } 392 lvaddr_t root_pt = local_phys_to_mem(dispatcher->vspace); 393 394#ifdef CONFIG_PAE 395 // loop over pdpt entries 396 for (int pdir_index = 0; pdir_index < X86_32_PDPTE_SIZE; pdir_index++) { 397 // get pdir 398 union x86_32_pdpte_entry *pdir = (union x86_32_pdpte_entry *)root_pt + pdir_index; 399 if (!pdir->raw) { continue; } 400 genpaddr_t pdir_gp = pdir->d.base_addr << BASE_PAGE_BITS; 401 lvaddr_t pdir_lv = local_phys_to_mem(gen_phys_to_local_phys(pdir_gp)); 402#else 403 int pdir_index = 0; 404 lvaddr_t pdir_lv = root_pt; 405#endif 406 407 // only go to 512 because upper half of address space is kernel space 408 // (1:1 mapped) 409 // TODO: figure out what we need to do here for PAE 410 for (int ptable_index = 0; ptable_index < 512; ptable_index++) { 411 // get ptable 412 union x86_32_pdir_entry *ptable = (union x86_32_pdir_entry *)pdir_lv + ptable_index; 413 union x86_32_ptable_entry *large = (union x86_32_ptable_entry *)ptable; 414 if (!ptable->raw) { continue; } 415 if (large->large.always1) { 416 // large page 417 genpaddr_t paddr = large->large.base_addr << X86_32_LARGE_PAGE_BITS; 418 printf("%d.%d: 0x%"PRIxGENPADDR"\n", pdir_index, 419 ptable_index, paddr); 420 } 421 genpaddr_t ptable_gp = ptable->d.base_addr << BASE_PAGE_BITS; 422 lvaddr_t ptable_lv = local_phys_to_mem(gen_phys_to_local_phys(ptable_gp)); 423 424 for (int entry = 0; entry < X86_32_PTABLE_SIZE; entry++) { 425 union x86_32_ptable_entry *e = 426 (union x86_32_ptable_entry *)ptable_lv + entry; 427 genpaddr_t paddr = (genpaddr_t)e->base.base_addr << BASE_PAGE_BITS; 428 if (!paddr) { 429 continue; 430 } 431 printf("%d.%d.%d: 0x%"PRIxGENPADDR"\n", pdir_index, ptable_index, entry, paddr); 432 } 433 } 434#ifdef CONFIG_PAE 435 } // endfor PDPT entries 436#endif 437} 438