1 /* 2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. 3 * Copyright 2002-2007, Axel Dörfler, axeld@pinc-software.de. All rights reserved. 4 * Distributed under the terms of the MIT License. 5 * 6 * Copyright 2001-2002, Travis Geiselbrecht. All rights reserved. 7 * Distributed under the terms of the NewOS License. 8 */ 9 10 #include <arch/vm_translation_map.h> 11 12 #include <stdlib.h> 13 #include <string.h> 14 15 #include <AutoDeleter.h> 16 17 #include <arch_system_info.h> 18 #include <heap.h> 19 #include <int.h> 20 #include <thread.h> 21 #include <smp.h> 22 #include <util/AutoLock.h> 23 #include <util/queue.h> 24 #include <vm_address_space.h> 25 #include <vm_page.h> 26 #include <vm_priv.h> 27 28 #include "x86_paging.h" 29 #include "x86_physical_page_mapper.h" 30 31 32 //#define TRACE_VM_TMAP 33 #ifdef TRACE_VM_TMAP 34 # define TRACE(x) dprintf x 35 #else 36 # define TRACE(x) ; 37 #endif 38 39 static page_table_entry *sPageHole = NULL; 40 static page_directory_entry *sPageHolePageDir = NULL; 41 static page_directory_entry *sKernelPhysicalPageDirectory = NULL; 42 static page_directory_entry *sKernelVirtualPageDirectory = NULL; 43 44 45 // Accessor class to reuse the SinglyLinkedListLink of DeferredDeletable for 46 // vm_translation_map_arch_info. 47 struct ArchTMapGetLink { 48 private: 49 typedef SinglyLinkedListLink<vm_translation_map_arch_info> Link; 50 51 public: 52 inline Link* operator()(vm_translation_map_arch_info* element) const 53 { 54 return (Link*)element->GetSinglyLinkedListLink(); 55 } 56 57 inline const Link* operator()( 58 const vm_translation_map_arch_info* element) const 59 { 60 return (const Link*)element->GetSinglyLinkedListLink(); 61 } 62 63 }; 64 65 66 typedef SinglyLinkedList<vm_translation_map_arch_info, ArchTMapGetLink> 67 ArchTMapList; 68 69 70 static ArchTMapList sTMapList; 71 static spinlock sTMapListLock; 72 73 #define CHATTY_TMAP 0 74 75 #define FIRST_USER_PGDIR_ENT (VADDR_TO_PDENT(USER_BASE)) 76 #define NUM_USER_PGDIR_ENTS (VADDR_TO_PDENT(ROUNDUP(USER_SIZE, \ 77 B_PAGE_SIZE * 1024))) 78 #define FIRST_KERNEL_PGDIR_ENT (VADDR_TO_PDENT(KERNEL_BASE)) 79 #define NUM_KERNEL_PGDIR_ENTS (VADDR_TO_PDENT(KERNEL_SIZE)) 80 #define IS_KERNEL_MAP(map) (map->arch_data->pgdir_phys \ 81 == sKernelPhysicalPageDirectory) 82 83 static status_t early_query(addr_t va, addr_t *out_physical); 84 85 static void flush_tmap(vm_translation_map *map); 86 87 88 void * 89 i386_translation_map_get_pgdir(vm_translation_map *map) 90 { 91 return map->arch_data->pgdir_phys; 92 } 93 94 95 void 96 x86_update_all_pgdirs(int index, page_directory_entry e) 97 { 98 unsigned int state = disable_interrupts(); 99 100 acquire_spinlock(&sTMapListLock); 101 102 ArchTMapList::Iterator it = sTMapList.GetIterator(); 103 while (vm_translation_map_arch_info* info = it.Next()) 104 info->pgdir_virt[index] = e; 105 106 release_spinlock(&sTMapListLock); 107 restore_interrupts(state); 108 } 109 110 111 // XXX currently assumes this translation map is active 112 113 static status_t 114 early_query(addr_t va, addr_t *_physicalAddress) 115 { 116 page_table_entry *pentry; 117 118 if (sPageHolePageDir[VADDR_TO_PDENT(va)].present == 0) { 119 // no pagetable here 120 return B_ERROR; 121 } 122 123 pentry = sPageHole + va / B_PAGE_SIZE; 124 if (pentry->present == 0) { 125 // page mapping not valid 126 return B_ERROR; 127 } 128 129 *_physicalAddress = pentry->addr << 12; 130 return B_OK; 131 } 132 133 134 /*! Acquires the map's recursive lock, and resets the invalidate pages counter 135 in case it's the first locking recursion. 136 */ 137 static status_t 138 lock_tmap(vm_translation_map *map) 139 { 140 TRACE(("lock_tmap: map %p\n", map)); 141 142 recursive_lock_lock(&map->lock); 143 if (recursive_lock_get_recursion(&map->lock) == 1) { 144 // we were the first one to grab the lock 145 TRACE(("clearing invalidated page count\n")); 146 map->arch_data->num_invalidate_pages = 0; 147 } 148 149 return B_OK; 150 } 151 152 153 /*! Unlocks the map, and, if we'll actually losing the recursive lock, 154 flush all pending changes of this map (ie. flush TLB caches as 155 needed). 156 */ 157 static status_t 158 unlock_tmap(vm_translation_map *map) 159 { 160 TRACE(("unlock_tmap: map %p\n", map)); 161 162 if (recursive_lock_get_recursion(&map->lock) == 1) { 163 // we're about to release it for the last time 164 flush_tmap(map); 165 } 166 167 recursive_lock_unlock(&map->lock); 168 return B_OK; 169 } 170 171 172 vm_translation_map_arch_info::vm_translation_map_arch_info() 173 : 174 pgdir_virt(NULL), 175 ref_count(1) 176 { 177 } 178 179 180 vm_translation_map_arch_info::~vm_translation_map_arch_info() 181 { 182 // free the page dir 183 free(pgdir_virt); 184 } 185 186 187 void 188 vm_translation_map_arch_info::Delete() 189 { 190 // remove from global list 191 InterruptsSpinLocker locker(sTMapListLock); 192 sTMapList.Remove(this); 193 locker.Unlock(); 194 195 if (are_interrupts_enabled()) 196 delete this; 197 else 198 deferred_delete(this); 199 } 200 201 202 static void 203 destroy_tmap(vm_translation_map *map) 204 { 205 if (map == NULL) 206 return; 207 208 if (map->arch_data->page_mapper != NULL) 209 map->arch_data->page_mapper->Delete(); 210 211 if (map->arch_data->pgdir_virt != NULL) { 212 // cycle through and free all of the user space pgtables 213 for (uint32 i = VADDR_TO_PDENT(USER_BASE); 214 i <= VADDR_TO_PDENT(USER_BASE + (USER_SIZE - 1)); i++) { 215 addr_t pgtable_addr; 216 vm_page *page; 217 218 if (map->arch_data->pgdir_virt[i].present == 1) { 219 pgtable_addr = map->arch_data->pgdir_virt[i].addr; 220 page = vm_lookup_page(pgtable_addr); 221 if (!page) 222 panic("destroy_tmap: didn't find pgtable page\n"); 223 vm_page_set_state(page, PAGE_STATE_FREE); 224 } 225 } 226 } 227 228 map->arch_data->RemoveReference(); 229 230 recursive_lock_destroy(&map->lock); 231 } 232 233 234 void 235 x86_put_pgtable_in_pgdir(page_directory_entry *entry, 236 addr_t pgtable_phys, uint32 attributes) 237 { 238 page_directory_entry table; 239 // put it in the pgdir 240 init_page_directory_entry(&table); 241 table.addr = ADDR_SHIFT(pgtable_phys); 242 243 // ToDo: we ignore the attributes of the page table - for compatibility 244 // with BeOS we allow having user accessible areas in the kernel address 245 // space. This is currently being used by some drivers, mainly for the 246 // frame buffer. Our current real time data implementation makes use of 247 // this fact, too. 248 // We might want to get rid of this possibility one day, especially if 249 // we intend to port it to a platform that does not support this. 250 table.user = 1; 251 table.rw = 1; 252 table.present = 1; 253 update_page_directory_entry(entry, &table); 254 } 255 256 257 static void 258 put_page_table_entry_in_pgtable(page_table_entry *entry, 259 addr_t physicalAddress, uint32 attributes, bool globalPage) 260 { 261 page_table_entry page; 262 init_page_table_entry(&page); 263 264 page.addr = ADDR_SHIFT(physicalAddress); 265 266 // if the page is user accessible, it's automatically 267 // accessible in kernel space, too (but with the same 268 // protection) 269 page.user = (attributes & B_USER_PROTECTION) != 0; 270 if (page.user) 271 page.rw = (attributes & B_WRITE_AREA) != 0; 272 else 273 page.rw = (attributes & B_KERNEL_WRITE_AREA) != 0; 274 page.present = 1; 275 276 if (globalPage) 277 page.global = 1; 278 279 // put it in the page table 280 update_page_table_entry(entry, &page); 281 } 282 283 284 static size_t 285 map_max_pages_need(vm_translation_map */*map*/, addr_t start, addr_t end) 286 { 287 // If start == 0, the actual base address is not yet known to the caller and 288 // we shall assume the worst case. 289 if (start == 0) { 290 start = 1023 * B_PAGE_SIZE; 291 end += 1023 * B_PAGE_SIZE; 292 } 293 return VADDR_TO_PDENT(end) + 1 - VADDR_TO_PDENT(start); 294 } 295 296 297 static status_t 298 map_tmap(vm_translation_map *map, addr_t va, addr_t pa, uint32 attributes) 299 { 300 page_directory_entry *pd; 301 page_table_entry *pt; 302 unsigned int index; 303 304 TRACE(("map_tmap: entry pa 0x%lx va 0x%lx\n", pa, va)); 305 306 /* 307 dprintf("pgdir at 0x%x\n", pgdir); 308 dprintf("index is %d\n", va / B_PAGE_SIZE / 1024); 309 dprintf("final at 0x%x\n", &pgdir[va / B_PAGE_SIZE / 1024]); 310 dprintf("value is 0x%x\n", *(int *)&pgdir[va / B_PAGE_SIZE / 1024]); 311 dprintf("present bit is %d\n", pgdir[va / B_PAGE_SIZE / 1024].present); 312 dprintf("addr is %d\n", pgdir[va / B_PAGE_SIZE / 1024].addr); 313 */ 314 pd = map->arch_data->pgdir_virt; 315 316 // check to see if a page table exists for this range 317 index = VADDR_TO_PDENT(va); 318 if (pd[index].present == 0) { 319 addr_t pgtable; 320 vm_page *page; 321 322 // we need to allocate a pgtable 323 page = vm_page_allocate_page(PAGE_STATE_CLEAR, true); 324 325 // mark the page WIRED 326 vm_page_set_state(page, PAGE_STATE_WIRED); 327 328 pgtable = page->physical_page_number * B_PAGE_SIZE; 329 330 TRACE(("map_tmap: asked for free page for pgtable. 0x%lx\n", pgtable)); 331 332 // put it in the pgdir 333 x86_put_pgtable_in_pgdir(&pd[index], pgtable, attributes 334 | ((attributes & B_USER_PROTECTION) != 0 335 ? B_WRITE_AREA : B_KERNEL_WRITE_AREA)); 336 337 // update any other page directories, if it maps kernel space 338 if (index >= FIRST_KERNEL_PGDIR_ENT 339 && index < (FIRST_KERNEL_PGDIR_ENT + NUM_KERNEL_PGDIR_ENTS)) 340 x86_update_all_pgdirs(index, pd[index]); 341 342 map->map_count++; 343 } 344 345 // now, fill in the pentry 346 struct thread* thread = thread_get_current_thread(); 347 ThreadCPUPinner pinner(thread); 348 349 pt = map->arch_data->page_mapper->GetPageTableAt( 350 ADDR_REVERSE_SHIFT(pd[index].addr)); 351 index = VADDR_TO_PTENT(va); 352 353 put_page_table_entry_in_pgtable(&pt[index], pa, attributes, 354 IS_KERNEL_MAP(map)); 355 356 pinner.Unlock(); 357 358 if (map->arch_data->num_invalidate_pages < PAGE_INVALIDATE_CACHE_SIZE) { 359 map->arch_data->pages_to_invalidate[ 360 map->arch_data->num_invalidate_pages] = va; 361 } 362 363 map->arch_data->num_invalidate_pages++; 364 365 map->map_count++; 366 367 return 0; 368 } 369 370 371 static status_t 372 unmap_tmap(vm_translation_map *map, addr_t start, addr_t end) 373 { 374 page_table_entry *pt; 375 page_directory_entry *pd = map->arch_data->pgdir_virt; 376 int index; 377 378 start = ROUNDOWN(start, B_PAGE_SIZE); 379 end = ROUNDUP(end, B_PAGE_SIZE); 380 381 TRACE(("unmap_tmap: asked to free pages 0x%lx to 0x%lx\n", start, end)); 382 383 restart: 384 if (start >= end) 385 return B_OK; 386 387 index = VADDR_TO_PDENT(start); 388 if (pd[index].present == 0) { 389 // no pagetable here, move the start up to access the next page table 390 start = ROUNDUP(start + 1, B_PAGE_SIZE); 391 goto restart; 392 } 393 394 struct thread* thread = thread_get_current_thread(); 395 ThreadCPUPinner pinner(thread); 396 397 pt = map->arch_data->page_mapper->GetPageTableAt( 398 ADDR_REVERSE_SHIFT(pd[index].addr)); 399 400 for (index = VADDR_TO_PTENT(start); (index < 1024) && (start < end); 401 index++, start += B_PAGE_SIZE) { 402 if (pt[index].present == 0) { 403 // page mapping not valid 404 continue; 405 } 406 407 TRACE(("unmap_tmap: removing page 0x%lx\n", start)); 408 409 pt[index].present = 0; 410 map->map_count--; 411 412 if (map->arch_data->num_invalidate_pages < PAGE_INVALIDATE_CACHE_SIZE) { 413 map->arch_data->pages_to_invalidate[ 414 map->arch_data->num_invalidate_pages] = start; 415 } 416 417 map->arch_data->num_invalidate_pages++; 418 } 419 420 pinner.Unlock(); 421 422 goto restart; 423 } 424 425 426 static status_t 427 query_tmap_interrupt(vm_translation_map *map, addr_t va, addr_t *_physical, 428 uint32 *_flags) 429 { 430 page_directory_entry *pd = map->arch_data->pgdir_virt; 431 page_table_entry *pt; 432 addr_t physicalPageTable; 433 int32 index; 434 435 *_physical = 0; 436 437 index = VADDR_TO_PDENT(va); 438 if (pd[index].present == 0) { 439 // no pagetable here 440 return B_ERROR; 441 } 442 443 // map page table entry 444 physicalPageTable = ADDR_REVERSE_SHIFT(pd[index].addr); 445 pt = gPhysicalPageMapper->InterruptGetPageTableAt(physicalPageTable); 446 447 index = VADDR_TO_PTENT(va); 448 *_physical = ADDR_REVERSE_SHIFT(pt[index].addr); 449 450 *_flags |= ((pt[index].rw ? B_KERNEL_WRITE_AREA : 0) | B_KERNEL_READ_AREA) 451 | (pt[index].dirty ? PAGE_MODIFIED : 0) 452 | (pt[index].accessed ? PAGE_ACCESSED : 0) 453 | (pt[index].present ? PAGE_PRESENT : 0); 454 455 return B_OK; 456 } 457 458 459 static status_t 460 query_tmap(vm_translation_map *map, addr_t va, addr_t *_physical, 461 uint32 *_flags) 462 { 463 page_table_entry *pt; 464 page_directory_entry *pd = map->arch_data->pgdir_virt; 465 int32 index; 466 467 // default the flags to not present 468 *_flags = 0; 469 *_physical = 0; 470 471 index = VADDR_TO_PDENT(va); 472 if (pd[index].present == 0) { 473 // no pagetable here 474 return B_NO_ERROR; 475 } 476 477 struct thread* thread = thread_get_current_thread(); 478 ThreadCPUPinner pinner(thread); 479 480 pt = map->arch_data->page_mapper->GetPageTableAt( 481 ADDR_REVERSE_SHIFT(pd[index].addr)); 482 index = VADDR_TO_PTENT(va); 483 484 *_physical = ADDR_REVERSE_SHIFT(pt[index].addr); 485 486 // read in the page state flags 487 if (pt[index].user) 488 *_flags |= (pt[index].rw ? B_WRITE_AREA : 0) | B_READ_AREA; 489 490 *_flags |= ((pt[index].rw ? B_KERNEL_WRITE_AREA : 0) | B_KERNEL_READ_AREA) 491 | (pt[index].dirty ? PAGE_MODIFIED : 0) 492 | (pt[index].accessed ? PAGE_ACCESSED : 0) 493 | (pt[index].present ? PAGE_PRESENT : 0); 494 495 pinner.Unlock(); 496 497 TRACE(("query_tmap: returning pa 0x%lx for va 0x%lx\n", *_physical, va)); 498 499 return B_OK; 500 } 501 502 503 static addr_t 504 get_mapped_size_tmap(vm_translation_map *map) 505 { 506 return map->map_count; 507 } 508 509 510 static status_t 511 protect_tmap(vm_translation_map *map, addr_t start, addr_t end, 512 uint32 attributes) 513 { 514 page_table_entry *pt; 515 page_directory_entry *pd = map->arch_data->pgdir_virt; 516 int index; 517 518 start = ROUNDOWN(start, B_PAGE_SIZE); 519 end = ROUNDUP(end, B_PAGE_SIZE); 520 521 TRACE(("protect_tmap: pages 0x%lx to 0x%lx, attributes %lx\n", start, end, 522 attributes)); 523 524 restart: 525 if (start >= end) 526 return B_OK; 527 528 index = VADDR_TO_PDENT(start); 529 if (pd[index].present == 0) { 530 // no pagetable here, move the start up to access the next page table 531 start = ROUNDUP(start + 1, B_PAGE_SIZE); 532 goto restart; 533 } 534 535 struct thread* thread = thread_get_current_thread(); 536 ThreadCPUPinner pinner(thread); 537 538 pt = map->arch_data->page_mapper->GetPageTableAt( 539 ADDR_REVERSE_SHIFT(pd[index].addr)); 540 541 for (index = VADDR_TO_PTENT(start); index < 1024 && start < end; 542 index++, start += B_PAGE_SIZE) { 543 if (pt[index].present == 0) { 544 // page mapping not valid 545 continue; 546 } 547 548 TRACE(("protect_tmap: protect page 0x%lx\n", start)); 549 550 pt[index].user = (attributes & B_USER_PROTECTION) != 0; 551 if ((attributes & B_USER_PROTECTION) != 0) 552 pt[index].rw = (attributes & B_WRITE_AREA) != 0; 553 else 554 pt[index].rw = (attributes & B_KERNEL_WRITE_AREA) != 0; 555 556 if (map->arch_data->num_invalidate_pages < PAGE_INVALIDATE_CACHE_SIZE) { 557 map->arch_data->pages_to_invalidate[ 558 map->arch_data->num_invalidate_pages] = start; 559 } 560 561 map->arch_data->num_invalidate_pages++; 562 } 563 564 pinner.Unlock(); 565 566 goto restart; 567 } 568 569 570 static status_t 571 clear_flags_tmap(vm_translation_map *map, addr_t va, uint32 flags) 572 { 573 page_table_entry *pt; 574 page_directory_entry *pd = map->arch_data->pgdir_virt; 575 int index; 576 int tlb_flush = false; 577 578 index = VADDR_TO_PDENT(va); 579 if (pd[index].present == 0) { 580 // no pagetable here 581 return B_OK; 582 } 583 584 struct thread* thread = thread_get_current_thread(); 585 ThreadCPUPinner pinner(thread); 586 587 pt = map->arch_data->page_mapper->GetPageTableAt( 588 ADDR_REVERSE_SHIFT(pd[index].addr)); 589 index = VADDR_TO_PTENT(va); 590 591 // clear out the flags we've been requested to clear 592 if (flags & PAGE_MODIFIED) { 593 pt[index].dirty = 0; 594 tlb_flush = true; 595 } 596 if (flags & PAGE_ACCESSED) { 597 pt[index].accessed = 0; 598 tlb_flush = true; 599 } 600 601 pinner.Unlock(); 602 603 if (tlb_flush) { 604 if (map->arch_data->num_invalidate_pages < PAGE_INVALIDATE_CACHE_SIZE) { 605 map->arch_data->pages_to_invalidate[ 606 map->arch_data->num_invalidate_pages] = va; 607 } 608 609 map->arch_data->num_invalidate_pages++; 610 } 611 612 return B_OK; 613 } 614 615 616 static void 617 flush_tmap(vm_translation_map *map) 618 { 619 if (map->arch_data->num_invalidate_pages <= 0) 620 return; 621 622 struct thread* thread = thread_get_current_thread(); 623 thread_pin_to_current_cpu(thread); 624 625 if (map->arch_data->num_invalidate_pages > PAGE_INVALIDATE_CACHE_SIZE) { 626 // invalidate all pages 627 TRACE(("flush_tmap: %d pages to invalidate, invalidate all\n", 628 map->arch_data->num_invalidate_pages)); 629 630 if (IS_KERNEL_MAP(map)) { 631 arch_cpu_global_TLB_invalidate(); 632 smp_send_broadcast_ici(SMP_MSG_GLOBAL_INVALIDATE_PAGES, 0, 0, 0, 633 NULL, SMP_MSG_FLAG_SYNC); 634 } else { 635 arch_cpu_user_TLB_invalidate(); 636 637 int cpu = smp_get_current_cpu(); 638 uint32 cpuMask = map->arch_data->active_on_cpus 639 & ~((uint32)1 << cpu); 640 if (cpuMask != 0) { 641 smp_send_multicast_ici(cpuMask, SMP_MSG_USER_INVALIDATE_PAGES, 642 0, 0, 0, NULL, SMP_MSG_FLAG_SYNC); 643 } 644 } 645 } else { 646 TRACE(("flush_tmap: %d pages to invalidate, invalidate list\n", 647 map->arch_data->num_invalidate_pages)); 648 649 arch_cpu_invalidate_TLB_list(map->arch_data->pages_to_invalidate, 650 map->arch_data->num_invalidate_pages); 651 652 if (IS_KERNEL_MAP(map)) { 653 smp_send_broadcast_ici(SMP_MSG_INVALIDATE_PAGE_LIST, 654 (uint32)map->arch_data->pages_to_invalidate, 655 map->arch_data->num_invalidate_pages, 0, NULL, 656 SMP_MSG_FLAG_SYNC); 657 } else { 658 int cpu = smp_get_current_cpu(); 659 uint32 cpuMask = map->arch_data->active_on_cpus 660 & ~((uint32)1 << cpu); 661 if (cpuMask != 0) { 662 smp_send_multicast_ici(cpuMask, SMP_MSG_INVALIDATE_PAGE_LIST, 663 (uint32)map->arch_data->pages_to_invalidate, 664 map->arch_data->num_invalidate_pages, 0, NULL, 665 SMP_MSG_FLAG_SYNC); 666 } 667 } 668 } 669 map->arch_data->num_invalidate_pages = 0; 670 671 thread_unpin_from_current_cpu(thread); 672 } 673 674 675 static vm_translation_map_ops tmap_ops = { 676 destroy_tmap, 677 lock_tmap, 678 unlock_tmap, 679 map_max_pages_need, 680 map_tmap, 681 unmap_tmap, 682 query_tmap, 683 query_tmap_interrupt, 684 get_mapped_size_tmap, 685 protect_tmap, 686 clear_flags_tmap, 687 flush_tmap 688 689 // The physical page ops are initialized by the respective physical page 690 // mapper. 691 }; 692 693 694 // #pragma mark - 695 696 697 void 698 x86_early_prepare_page_tables(page_table_entry* pageTables, addr_t address, 699 size_t size) 700 { 701 memset(pageTables, 0, B_PAGE_SIZE * (size / (B_PAGE_SIZE * 1024))); 702 703 // put the array of pgtables directly into the kernel pagedir 704 // these will be wired and kept mapped into virtual space to be easy to get 705 // to 706 { 707 addr_t virtualTable = (addr_t)pageTables; 708 709 for (size_t i = 0; i < (size / (B_PAGE_SIZE * 1024)); 710 i++, virtualTable += B_PAGE_SIZE) { 711 addr_t physicalTable; 712 early_query(virtualTable, &physicalTable); 713 page_directory_entry* entry = &sPageHolePageDir[ 714 (address / (B_PAGE_SIZE * 1024)) + i]; 715 x86_put_pgtable_in_pgdir(entry, physicalTable, 716 B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 717 } 718 } 719 } 720 721 722 // #pragma mark - 723 // VM API 724 725 726 status_t 727 arch_vm_translation_map_init_map(vm_translation_map *map, bool kernel) 728 { 729 if (map == NULL) 730 return B_BAD_VALUE; 731 732 TRACE(("vm_translation_map_create\n")); 733 734 // initialize the new object 735 map->ops = &tmap_ops; 736 map->map_count = 0; 737 738 recursive_lock_init(&map->lock, "translation map"); 739 CObjectDeleter<recursive_lock> lockDeleter(&map->lock, 740 &recursive_lock_destroy); 741 742 map->arch_data = new(std::nothrow) vm_translation_map_arch_info; 743 if (map->arch_data == NULL) 744 return B_NO_MEMORY; 745 ObjectDeleter<vm_translation_map_arch_info> archInfoDeleter(map->arch_data); 746 747 map->arch_data->active_on_cpus = 0; 748 map->arch_data->num_invalidate_pages = 0; 749 map->arch_data->page_mapper = NULL; 750 751 if (!kernel) { 752 // user 753 // allocate a physical page mapper 754 status_t error = gPhysicalPageMapper 755 ->CreateTranslationMapPhysicalPageMapper( 756 &map->arch_data->page_mapper); 757 if (error != B_OK) 758 return error; 759 760 // allocate a pgdir 761 map->arch_data->pgdir_virt = (page_directory_entry *)memalign( 762 B_PAGE_SIZE, B_PAGE_SIZE); 763 if (map->arch_data->pgdir_virt == NULL) { 764 map->arch_data->page_mapper->Delete(); 765 return B_NO_MEMORY; 766 } 767 vm_get_page_mapping(vm_kernel_address_space_id(), 768 (addr_t)map->arch_data->pgdir_virt, 769 (addr_t*)&map->arch_data->pgdir_phys); 770 } else { 771 // kernel 772 // get the physical page mapper 773 map->arch_data->page_mapper = gKernelPhysicalPageMapper; 774 775 // we already know the kernel pgdir mapping 776 map->arch_data->pgdir_virt = sKernelVirtualPageDirectory; 777 map->arch_data->pgdir_phys = sKernelPhysicalPageDirectory; 778 } 779 780 // zero out the bottom portion of the new pgdir 781 memset(map->arch_data->pgdir_virt + FIRST_USER_PGDIR_ENT, 0, 782 NUM_USER_PGDIR_ENTS * sizeof(page_directory_entry)); 783 784 // insert this new map into the map list 785 { 786 int state = disable_interrupts(); 787 acquire_spinlock(&sTMapListLock); 788 789 // copy the top portion of the pgdir from the current one 790 memcpy(map->arch_data->pgdir_virt + FIRST_KERNEL_PGDIR_ENT, 791 sKernelVirtualPageDirectory + FIRST_KERNEL_PGDIR_ENT, 792 NUM_KERNEL_PGDIR_ENTS * sizeof(page_directory_entry)); 793 794 sTMapList.Add(map->arch_data); 795 796 release_spinlock(&sTMapListLock); 797 restore_interrupts(state); 798 } 799 800 archInfoDeleter.Detach(); 801 lockDeleter.Detach(); 802 803 return B_OK; 804 } 805 806 807 status_t 808 arch_vm_translation_map_init_kernel_map_post_sem(vm_translation_map *map) 809 { 810 return B_OK; 811 } 812 813 814 status_t 815 arch_vm_translation_map_init(kernel_args *args) 816 { 817 TRACE(("vm_translation_map_init: entry\n")); 818 819 // page hole set up in stage2 820 sPageHole = (page_table_entry *)args->arch_args.page_hole; 821 // calculate where the pgdir would be 822 sPageHolePageDir = (page_directory_entry*) 823 (((addr_t)args->arch_args.page_hole) 824 + (B_PAGE_SIZE * 1024 - B_PAGE_SIZE)); 825 // clear out the bottom 2 GB, unmap everything 826 memset(sPageHolePageDir + FIRST_USER_PGDIR_ENT, 0, 827 sizeof(page_directory_entry) * NUM_USER_PGDIR_ENTS); 828 829 sKernelPhysicalPageDirectory = (page_directory_entry*) 830 args->arch_args.phys_pgdir; 831 sKernelVirtualPageDirectory = (page_directory_entry*) 832 args->arch_args.vir_pgdir; 833 834 B_INITIALIZE_SPINLOCK(&sTMapListLock); 835 new (&sTMapList) ArchTMapList; 836 837 // TODO: Select the best page mapper! 838 large_memory_physical_page_ops_init(args, &tmap_ops); 839 840 // enable global page feature if available 841 if (x86_check_feature(IA32_FEATURE_PGE, FEATURE_COMMON)) { 842 // this prevents kernel pages from being flushed from TLB on 843 // context-switch 844 x86_write_cr4(x86_read_cr4() | IA32_CR4_GLOBAL_PAGES); 845 } 846 847 TRACE(("vm_translation_map_init: done\n")); 848 849 return B_OK; 850 } 851 852 853 status_t 854 arch_vm_translation_map_init_post_sem(kernel_args *args) 855 { 856 return B_OK; 857 } 858 859 860 status_t 861 arch_vm_translation_map_init_post_area(kernel_args *args) 862 { 863 // now that the vm is initialized, create a region that represents 864 // the page hole 865 void *temp; 866 status_t error; 867 area_id area; 868 869 TRACE(("vm_translation_map_init_post_area: entry\n")); 870 871 // unmap the page hole hack we were using before 872 sKernelVirtualPageDirectory[1023].present = 0; 873 sPageHolePageDir = NULL; 874 sPageHole = NULL; 875 876 temp = (void *)sKernelVirtualPageDirectory; 877 area = create_area("kernel_pgdir", &temp, B_EXACT_ADDRESS, B_PAGE_SIZE, 878 B_ALREADY_WIRED, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA); 879 if (area < B_OK) 880 return area; 881 882 error = gPhysicalPageMapper->InitPostArea(args); 883 if (error != B_OK) 884 return error; 885 886 TRACE(("vm_translation_map_init_post_area: done\n")); 887 return B_OK; 888 } 889 890 891 // XXX horrible back door to map a page quickly regardless of translation map 892 // object, etc. 893 // used only during VM setup. 894 // uses a 'page hole' set up in the stage 2 bootloader. The page hole is created 895 // by pointing one of the pgdir entries back at itself, effectively mapping the 896 // contents of all of the 4MB of pagetables into a 4 MB region. It's only used 897 // here, and is later unmapped. 898 899 status_t 900 arch_vm_translation_map_early_map(kernel_args *args, addr_t va, addr_t pa, 901 uint8 attributes, addr_t (*get_free_page)(kernel_args *)) 902 { 903 int index; 904 905 TRACE(("early_tmap: entry pa 0x%lx va 0x%lx\n", pa, va)); 906 907 // check to see if a page table exists for this range 908 index = VADDR_TO_PDENT(va); 909 if (sPageHolePageDir[index].present == 0) { 910 addr_t pgtable; 911 page_directory_entry *e; 912 // we need to allocate a pgtable 913 pgtable = get_free_page(args); 914 // pgtable is in pages, convert to physical address 915 pgtable *= B_PAGE_SIZE; 916 917 TRACE(("early_map: asked for free page for pgtable. 0x%lx\n", pgtable)); 918 919 // put it in the pgdir 920 e = &sPageHolePageDir[index]; 921 x86_put_pgtable_in_pgdir(e, pgtable, attributes); 922 923 // zero it out in it's new mapping 924 memset((unsigned int*)((addr_t)sPageHole 925 + (va / B_PAGE_SIZE / 1024) * B_PAGE_SIZE), 0, B_PAGE_SIZE); 926 } 927 928 // now, fill in the pentry 929 put_page_table_entry_in_pgtable(sPageHole + va / B_PAGE_SIZE, pa, 930 attributes, IS_KERNEL_ADDRESS(va)); 931 932 arch_cpu_invalidate_TLB_range(va, va); 933 934 return B_OK; 935 } 936 937