Serenity Operating System
at portability 722 lines 25 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include "CMOS.h" 28#include "Process.h" 29#include <AK/Assertions.h> 30#include <Kernel/Arch/i386/CPU.h> 31#include <Kernel/FileSystem/Inode.h> 32#include <Kernel/Multiboot.h> 33#include <Kernel/VM/AnonymousVMObject.h> 34#include <Kernel/VM/InodeVMObject.h> 35#include <Kernel/VM/MemoryManager.h> 36#include <Kernel/VM/PageDirectory.h> 37#include <Kernel/VM/PhysicalRegion.h> 38#include <Kernel/VM/PurgeableVMObject.h> 39#include <LibBareMetal/StdLib.h> 40 41//#define MM_DEBUG 42//#define PAGE_FAULT_DEBUG 43 44extern uintptr_t start_of_kernel_text; 45extern uintptr_t start_of_kernel_data; 46extern uintptr_t end_of_kernel_bss; 47 48namespace Kernel { 49 50static MemoryManager* s_the; 51 52MemoryManager& MM 53{ 54 return *s_the; 55} 56 57MemoryManager::MemoryManager() 58{ 59 m_kernel_page_directory = PageDirectory::create_kernel_page_directory(); 60 parse_memory_map(); 61 write_cr3(kernel_page_directory().cr3()); 62 setup_low_identity_mapping(); 63 protect_kernel_image(); 64 65 m_shared_zero_page = allocate_user_physical_page(); 66} 67 68MemoryManager::~MemoryManager() 69{ 70} 71 72void MemoryManager::protect_kernel_image() 73{ 74 // Disable writing to the kernel text and rodata segments. 75 for (size_t i = (uintptr_t)&start_of_kernel_text; i < (uintptr_t)&start_of_kernel_data; i += PAGE_SIZE) { 76 auto& pte = ensure_pte(kernel_page_directory(), VirtualAddress(i)); 77 pte.set_writable(false); 78 } 79 80 if (g_cpu_supports_nx) { 81 // Disable execution of the kernel data and bss segments. 82 for (size_t i = (uintptr_t)&start_of_kernel_data; i < (uintptr_t)&end_of_kernel_bss; i += PAGE_SIZE) { 83 auto& pte = ensure_pte(kernel_page_directory(), VirtualAddress(i)); 84 pte.set_execute_disabled(true); 85 } 86 } 87} 88 89void MemoryManager::setup_low_identity_mapping() 90{ 91 m_low_page_table = allocate_user_physical_page(ShouldZeroFill::Yes); 92 93 auto* pd_zero = quickmap_pd(kernel_page_directory(), 0); 94 pd_zero[1].set_present(false); 95 pd_zero[2].set_present(false); 96 pd_zero[3].set_present(false); 97 98 auto& pde_zero = pd_zero[0]; 99 pde_zero.set_page_table_base(m_low_page_table->paddr().get()); 100 pde_zero.set_present(true); 101 pde_zero.set_huge(false); 102 pde_zero.set_writable(true); 103 pde_zero.set_user_allowed(false); 104 if (g_cpu_supports_nx) 105 pde_zero.set_execute_disabled(true); 106 107 for (uintptr_t offset = (1 * MB); offset < (2 * MB); offset += PAGE_SIZE) { 108 auto& page_table_page = m_low_page_table; 109 auto& pte = quickmap_pt(page_table_page->paddr())[offset / PAGE_SIZE]; 110 pte.set_physical_page_base(offset); 111 pte.set_user_allowed(false); 112 pte.set_present(offset != 0); 113 pte.set_writable(offset < (1 * MB)); 114 } 115} 116 117void MemoryManager::parse_memory_map() 118{ 119 RefPtr<PhysicalRegion> region; 120 bool region_is_super = false; 121 122 auto* mmap = (multiboot_memory_map_t*)(low_physical_to_virtual(multiboot_info_ptr->mmap_addr)); 123 for (; (unsigned long)mmap < (low_physical_to_virtual(multiboot_info_ptr->mmap_addr)) + (multiboot_info_ptr->mmap_length); mmap = (multiboot_memory_map_t*)((unsigned long)mmap + mmap->size + sizeof(mmap->size))) { 124 kprintf("MM: Multiboot mmap: base_addr = 0x%x%08x, length = 0x%x%08x, type = 0x%x\n", 125 (uintptr_t)(mmap->addr >> 32), 126 (uintptr_t)(mmap->addr & 0xffffffff), 127 (uintptr_t)(mmap->len >> 32), 128 (uintptr_t)(mmap->len & 0xffffffff), 129 (uintptr_t)mmap->type); 130 131 if (mmap->type != MULTIBOOT_MEMORY_AVAILABLE) 132 continue; 133 134 // FIXME: Maybe make use of stuff below the 1MB mark? 135 if (mmap->addr < (1 * MB)) 136 continue; 137 138 if ((mmap->addr + mmap->len) > 0xffffffff) 139 continue; 140 141 auto diff = (uintptr_t)mmap->addr % PAGE_SIZE; 142 if (diff != 0) { 143 kprintf("MM: got an unaligned region base from the bootloader; correcting %p by %d bytes\n", mmap->addr, diff); 144 diff = PAGE_SIZE - diff; 145 mmap->addr += diff; 146 mmap->len -= diff; 147 } 148 if ((mmap->len % PAGE_SIZE) != 0) { 149 kprintf("MM: got an unaligned region length from the bootloader; correcting %d by %d bytes\n", mmap->len, mmap->len % PAGE_SIZE); 150 mmap->len -= mmap->len % PAGE_SIZE; 151 } 152 if (mmap->len < PAGE_SIZE) { 153 kprintf("MM: memory region from bootloader is too small; we want >= %d bytes, but got %d bytes\n", PAGE_SIZE, mmap->len); 154 continue; 155 } 156 157#ifdef MM_DEBUG 158 kprintf("MM: considering memory at %p - %p\n", 159 (uintptr_t)mmap->addr, (uintptr_t)(mmap->addr + mmap->len)); 160#endif 161 162 for (size_t page_base = mmap->addr; page_base < (mmap->addr + mmap->len); page_base += PAGE_SIZE) { 163 auto addr = PhysicalAddress(page_base); 164 165 if (page_base < 7 * MB) { 166 // nothing 167 } else if (page_base >= 7 * MB && page_base < 8 * MB) { 168 if (region.is_null() || !region_is_super || region->upper().offset(PAGE_SIZE) != addr) { 169 m_super_physical_regions.append(PhysicalRegion::create(addr, addr)); 170 region = m_super_physical_regions.last(); 171 region_is_super = true; 172 } else { 173 region->expand(region->lower(), addr); 174 } 175 } else { 176 if (region.is_null() || region_is_super || region->upper().offset(PAGE_SIZE) != addr) { 177 m_user_physical_regions.append(PhysicalRegion::create(addr, addr)); 178 region = m_user_physical_regions.last(); 179 region_is_super = false; 180 } else { 181 region->expand(region->lower(), addr); 182 } 183 } 184 } 185 } 186 187 for (auto& region : m_super_physical_regions) 188 m_super_physical_pages += region.finalize_capacity(); 189 190 for (auto& region : m_user_physical_regions) 191 m_user_physical_pages += region.finalize_capacity(); 192} 193 194const PageTableEntry* MemoryManager::pte(const PageDirectory& page_directory, VirtualAddress vaddr) 195{ 196 ASSERT_INTERRUPTS_DISABLED(); 197 u32 page_directory_table_index = (vaddr.get() >> 30) & 0x3; 198 u32 page_directory_index = (vaddr.get() >> 21) & 0x1ff; 199 u32 page_table_index = (vaddr.get() >> 12) & 0x1ff; 200 201 auto* pd = quickmap_pd(const_cast<PageDirectory&>(page_directory), page_directory_table_index); 202 const PageDirectoryEntry& pde = pd[page_directory_index]; 203 if (!pde.is_present()) 204 return nullptr; 205 206 return &quickmap_pt(PhysicalAddress((uintptr_t)pde.page_table_base()))[page_table_index]; 207} 208 209PageTableEntry& MemoryManager::ensure_pte(PageDirectory& page_directory, VirtualAddress vaddr) 210{ 211 ASSERT_INTERRUPTS_DISABLED(); 212 u32 page_directory_table_index = (vaddr.get() >> 30) & 0x3; 213 u32 page_directory_index = (vaddr.get() >> 21) & 0x1ff; 214 u32 page_table_index = (vaddr.get() >> 12) & 0x1ff; 215 216 auto* pd = quickmap_pd(page_directory, page_directory_table_index); 217 PageDirectoryEntry& pde = pd[page_directory_index]; 218 if (!pde.is_present()) { 219#ifdef MM_DEBUG 220 dbgprintf("MM: PDE %u not present (requested for V%p), allocating\n", page_directory_index, vaddr.get()); 221#endif 222 auto page_table = allocate_user_physical_page(ShouldZeroFill::Yes); 223#ifdef MM_DEBUG 224 dbgprintf("MM: PD K%p (%s) at P%p allocated page table #%u (for V%p) at P%p\n", 225 &page_directory, 226 &page_directory == m_kernel_page_directory ? "Kernel" : "User", 227 page_directory.cr3(), 228 page_directory_index, 229 vaddr.get(), 230 page_table->paddr().get()); 231#endif 232 pde.set_page_table_base(page_table->paddr().get()); 233 pde.set_user_allowed(true); 234 pde.set_present(true); 235 pde.set_writable(true); 236 pde.set_global(&page_directory == m_kernel_page_directory.ptr()); 237 page_directory.m_physical_pages.set(page_directory_index, move(page_table)); 238 } 239 240 return quickmap_pt(PhysicalAddress((uintptr_t)pde.page_table_base()))[page_table_index]; 241} 242 243void MemoryManager::initialize() 244{ 245 s_the = new MemoryManager; 246} 247 248Region* MemoryManager::kernel_region_from_vaddr(VirtualAddress vaddr) 249{ 250 if (vaddr.get() < 0xc0000000) 251 return nullptr; 252 for (auto& region : MM.m_kernel_regions) { 253 if (region.contains(vaddr)) 254 return &region; 255 } 256 return nullptr; 257} 258 259Region* MemoryManager::user_region_from_vaddr(Process& process, VirtualAddress vaddr) 260{ 261 // FIXME: Use a binary search tree (maybe red/black?) or some other more appropriate data structure! 262 for (auto& region : process.m_regions) { 263 if (region.contains(vaddr)) 264 return &region; 265 } 266#ifdef MM_DEBUG 267 dbg() << process << " Couldn't find user region for " << vaddr; 268#endif 269 return nullptr; 270} 271 272Region* MemoryManager::region_from_vaddr(Process& process, VirtualAddress vaddr) 273{ 274 if (auto* region = kernel_region_from_vaddr(vaddr)) 275 return region; 276 return user_region_from_vaddr(process, vaddr); 277} 278 279const Region* MemoryManager::region_from_vaddr(const Process& process, VirtualAddress vaddr) 280{ 281 if (auto* region = kernel_region_from_vaddr(vaddr)) 282 return region; 283 return user_region_from_vaddr(const_cast<Process&>(process), vaddr); 284} 285 286Region* MemoryManager::region_from_vaddr(VirtualAddress vaddr) 287{ 288 if (auto* region = kernel_region_from_vaddr(vaddr)) 289 return region; 290 auto page_directory = PageDirectory::find_by_cr3(read_cr3()); 291 if (!page_directory) 292 return nullptr; 293 ASSERT(page_directory->process()); 294 return user_region_from_vaddr(*page_directory->process(), vaddr); 295} 296 297PageFaultResponse MemoryManager::handle_page_fault(const PageFault& fault) 298{ 299 ASSERT_INTERRUPTS_DISABLED(); 300 ASSERT(Thread::current); 301 if (g_in_irq) { 302 dbg() << "BUG! Page fault while handling IRQ! code=" << fault.code() << ", vaddr=" << fault.vaddr(); 303 dump_kernel_regions(); 304 } 305#ifdef PAGE_FAULT_DEBUG 306 dbgprintf("MM: handle_page_fault(%w) at V%p\n", fault.code(), fault.vaddr().get()); 307#endif 308 auto* region = region_from_vaddr(fault.vaddr()); 309 if (!region) { 310 kprintf("NP(error) fault at invalid address V%p\n", fault.vaddr().get()); 311 return PageFaultResponse::ShouldCrash; 312 } 313 314 return region->handle_fault(fault); 315} 316 317OwnPtr<Region> MemoryManager::allocate_kernel_region(size_t size, const StringView& name, u8 access, bool user_accessible, bool should_commit, bool cacheable) 318{ 319 InterruptDisabler disabler; 320 ASSERT(!(size % PAGE_SIZE)); 321 auto range = kernel_page_directory().range_allocator().allocate_anywhere(size); 322 ASSERT(range.is_valid()); 323 OwnPtr<Region> region; 324 if (user_accessible) 325 region = Region::create_user_accessible(range, name, access, cacheable); 326 else 327 region = Region::create_kernel_only(range, name, access, cacheable); 328 region->map(kernel_page_directory()); 329 if (should_commit) 330 region->commit(); 331 return region; 332} 333 334OwnPtr<Region> MemoryManager::allocate_kernel_region(PhysicalAddress paddr, size_t size, const StringView& name, u8 access, bool user_accessible, bool cacheable) 335{ 336 InterruptDisabler disabler; 337 ASSERT(!(size % PAGE_SIZE)); 338 auto range = kernel_page_directory().range_allocator().allocate_anywhere(size); 339 ASSERT(range.is_valid()); 340 auto vmobject = AnonymousVMObject::create_for_physical_range(paddr, size); 341 if (!vmobject) 342 return nullptr; 343 OwnPtr<Region> region; 344 if (user_accessible) 345 region = Region::create_user_accessible(range, vmobject.release_nonnull(), 0, name, access, cacheable); 346 else 347 region = Region::create_kernel_only(range, vmobject.release_nonnull(), 0, name, access, cacheable); 348 region->map(kernel_page_directory()); 349 return region; 350} 351 352OwnPtr<Region> MemoryManager::allocate_user_accessible_kernel_region(size_t size, const StringView& name, u8 access, bool cacheable) 353{ 354 return allocate_kernel_region(size, name, access, true, true, cacheable); 355} 356 357OwnPtr<Region> MemoryManager::allocate_kernel_region_with_vmobject(VMObject& vmobject, size_t size, const StringView& name, u8 access, bool user_accessible, bool cacheable) 358{ 359 InterruptDisabler disabler; 360 ASSERT(!(size % PAGE_SIZE)); 361 auto range = kernel_page_directory().range_allocator().allocate_anywhere(size); 362 ASSERT(range.is_valid()); 363 OwnPtr<Region> region; 364 if (user_accessible) 365 region = Region::create_user_accessible(range, vmobject, 0, name, access, cacheable); 366 else 367 region = Region::create_kernel_only(range, vmobject, 0, name, access, cacheable); 368 region->map(kernel_page_directory()); 369 return region; 370} 371 372void MemoryManager::deallocate_user_physical_page(PhysicalPage&& page) 373{ 374 for (auto& region : m_user_physical_regions) { 375 if (!region.contains(page)) { 376 kprintf( 377 "MM: deallocate_user_physical_page: %p not in %p -> %p\n", 378 page.paddr().get(), region.lower().get(), region.upper().get()); 379 continue; 380 } 381 382 region.return_page(move(page)); 383 --m_user_physical_pages_used; 384 385 return; 386 } 387 388 kprintf("MM: deallocate_user_physical_page couldn't figure out region for user page @ %p\n", page.paddr().get()); 389 ASSERT_NOT_REACHED(); 390} 391 392RefPtr<PhysicalPage> MemoryManager::find_free_user_physical_page() 393{ 394 RefPtr<PhysicalPage> page; 395 for (auto& region : m_user_physical_regions) { 396 page = region.take_free_page(false); 397 if (!page.is_null()) 398 break; 399 } 400 return page; 401} 402 403RefPtr<PhysicalPage> MemoryManager::allocate_user_physical_page(ShouldZeroFill should_zero_fill) 404{ 405 InterruptDisabler disabler; 406 RefPtr<PhysicalPage> page = find_free_user_physical_page(); 407 408 if (!page) { 409 if (m_user_physical_regions.is_empty()) { 410 kprintf("MM: no user physical regions available (?)\n"); 411 } 412 413 for_each_vmobject([&](auto& vmobject) { 414 if (vmobject.is_purgeable()) { 415 auto& purgeable_vmobject = static_cast<PurgeableVMObject&>(vmobject); 416 int purged_page_count = purgeable_vmobject.purge_with_interrupts_disabled({}); 417 if (purged_page_count) { 418 kprintf("MM: Purge saved the day! Purged %d pages from PurgeableVMObject{%p}\n", purged_page_count, &purgeable_vmobject); 419 page = find_free_user_physical_page(); 420 ASSERT(page); 421 return IterationDecision::Break; 422 } 423 } 424 return IterationDecision::Continue; 425 }); 426 427 if (!page) { 428 kprintf("MM: no user physical pages available\n"); 429 ASSERT_NOT_REACHED(); 430 return {}; 431 } 432 } 433 434#ifdef MM_DEBUG 435 dbgprintf("MM: allocate_user_physical_page vending P%p\n", page->paddr().get()); 436#endif 437 438 if (should_zero_fill == ShouldZeroFill::Yes) { 439 auto* ptr = quickmap_page(*page); 440 memset(ptr, 0, PAGE_SIZE); 441 unquickmap_page(); 442 } 443 444 ++m_user_physical_pages_used; 445 return page; 446} 447 448void MemoryManager::deallocate_supervisor_physical_page(PhysicalPage&& page) 449{ 450 for (auto& region : m_super_physical_regions) { 451 if (!region.contains(page)) { 452 kprintf( 453 "MM: deallocate_supervisor_physical_page: %p not in %p -> %p\n", 454 page.paddr().get(), region.lower().get(), region.upper().get()); 455 continue; 456 } 457 458 region.return_page(move(page)); 459 --m_super_physical_pages_used; 460 return; 461 } 462 463 kprintf("MM: deallocate_supervisor_physical_page couldn't figure out region for super page @ %p\n", page.paddr().get()); 464 ASSERT_NOT_REACHED(); 465} 466 467RefPtr<PhysicalPage> MemoryManager::allocate_supervisor_physical_page() 468{ 469 InterruptDisabler disabler; 470 RefPtr<PhysicalPage> page; 471 472 for (auto& region : m_super_physical_regions) { 473 page = region.take_free_page(true); 474 if (page.is_null()) 475 continue; 476 } 477 478 if (!page) { 479 if (m_super_physical_regions.is_empty()) { 480 kprintf("MM: no super physical regions available (?)\n"); 481 } 482 483 kprintf("MM: no super physical pages available\n"); 484 ASSERT_NOT_REACHED(); 485 return {}; 486 } 487 488#ifdef MM_DEBUG 489 dbgprintf("MM: allocate_supervisor_physical_page vending P%p\n", page->paddr().get()); 490#endif 491 492 fast_u32_fill((u32*)page->paddr().offset(0xc0000000).as_ptr(), 0, PAGE_SIZE / sizeof(u32)); 493 ++m_super_physical_pages_used; 494 return page; 495} 496 497void MemoryManager::enter_process_paging_scope(Process& process) 498{ 499 ASSERT(Thread::current); 500 InterruptDisabler disabler; 501 502 Thread::current->tss().cr3 = process.page_directory().cr3(); 503 write_cr3(process.page_directory().cr3()); 504} 505 506void MemoryManager::flush_entire_tlb() 507{ 508 write_cr3(read_cr3()); 509} 510 511void MemoryManager::flush_tlb(VirtualAddress vaddr) 512{ 513#ifdef MM_DEBUG 514 dbgprintf("MM: Flush page V%p\n", vaddr.get()); 515#endif 516 asm volatile("invlpg %0" 517 : 518 : "m"(*(char*)vaddr.get()) 519 : "memory"); 520} 521 522extern "C" PageTableEntry boot_pd3_pde1023_pt[1024]; 523 524PageDirectoryEntry* MemoryManager::quickmap_pd(PageDirectory& directory, size_t pdpt_index) 525{ 526 auto& pte = boot_pd3_pde1023_pt[4]; 527 auto pd_paddr = directory.m_directory_pages[pdpt_index]->paddr(); 528 if (pte.physical_page_base() != pd_paddr.as_ptr()) { 529#ifdef MM_DEBUG 530 dbgprintf("quickmap_pd: Mapping P%p at 0xffe04000 in pte @ %p\n", directory.m_directory_pages[pdpt_index]->paddr().as_ptr(), &pte); 531#endif 532 pte.set_physical_page_base(pd_paddr.get()); 533 pte.set_present(true); 534 pte.set_writable(true); 535 pte.set_user_allowed(false); 536 flush_tlb(VirtualAddress(0xffe04000)); 537 } 538 return (PageDirectoryEntry*)0xffe04000; 539} 540 541PageTableEntry* MemoryManager::quickmap_pt(PhysicalAddress pt_paddr) 542{ 543 auto& pte = boot_pd3_pde1023_pt[8]; 544 if (pte.physical_page_base() != pt_paddr.as_ptr()) { 545#ifdef MM_DEBUG 546 dbgprintf("quickmap_pt: Mapping P%p at 0xffe08000 in pte @ %p\n", pt_paddr.as_ptr(), &pte); 547#endif 548 pte.set_physical_page_base(pt_paddr.get()); 549 pte.set_present(true); 550 pte.set_writable(true); 551 pte.set_user_allowed(false); 552 flush_tlb(VirtualAddress(0xffe08000)); 553 } 554 return (PageTableEntry*)0xffe08000; 555} 556 557u8* MemoryManager::quickmap_page(PhysicalPage& physical_page) 558{ 559 ASSERT_INTERRUPTS_DISABLED(); 560 ASSERT(!m_quickmap_in_use); 561 m_quickmap_in_use = true; 562 563 auto& pte = boot_pd3_pde1023_pt[0]; 564 if (pte.physical_page_base() != physical_page.paddr().as_ptr()) { 565#ifdef MM_DEBUG 566 dbgprintf("quickmap_page: Mapping P%p at 0xffe00000 in pte @ %p\n", physical_page.paddr().as_ptr(), &pte); 567#endif 568 pte.set_physical_page_base(physical_page.paddr().get()); 569 pte.set_present(true); 570 pte.set_writable(true); 571 pte.set_user_allowed(false); 572 flush_tlb(VirtualAddress(0xffe00000)); 573 } 574 return (u8*)0xffe00000; 575} 576 577void MemoryManager::unquickmap_page() 578{ 579 ASSERT_INTERRUPTS_DISABLED(); 580 ASSERT(m_quickmap_in_use); 581 auto& pte = boot_pd3_pde1023_pt[0]; 582 pte.clear(); 583 flush_tlb(VirtualAddress(0xffe00000)); 584 m_quickmap_in_use = false; 585} 586 587template<MemoryManager::AccessSpace space, MemoryManager::AccessType access_type> 588bool MemoryManager::validate_range(const Process& process, VirtualAddress base_vaddr, size_t size) const 589{ 590 ASSERT(size); 591 if (base_vaddr > base_vaddr.offset(size)) { 592 dbg() << "Shenanigans! Asked to validate wrappy " << base_vaddr << " size=" << size; 593 return false; 594 } 595 596 VirtualAddress vaddr = base_vaddr.page_base(); 597 VirtualAddress end_vaddr = base_vaddr.offset(size - 1).page_base(); 598 if (end_vaddr < vaddr) { 599 dbg() << "Shenanigans! Asked to validate " << base_vaddr << " size=" << size; 600 return false; 601 } 602 const Region* region = nullptr; 603 while (vaddr <= end_vaddr) { 604 if (!region || !region->contains(vaddr)) { 605 if (space == AccessSpace::Kernel) 606 region = kernel_region_from_vaddr(vaddr); 607 if (!region || !region->contains(vaddr)) 608 region = user_region_from_vaddr(const_cast<Process&>(process), vaddr); 609 if (!region 610 || (space == AccessSpace::User && !region->is_user_accessible()) 611 || (access_type == AccessType::Read && !region->is_readable()) 612 || (access_type == AccessType::Write && !region->is_writable())) { 613 return false; 614 } 615 } 616 vaddr = vaddr.offset(PAGE_SIZE); 617 } 618 return true; 619} 620 621bool MemoryManager::validate_user_stack(const Process& process, VirtualAddress vaddr) const 622{ 623 if (!is_user_address(vaddr)) 624 return false; 625 auto* region = user_region_from_vaddr(const_cast<Process&>(process), vaddr); 626 return region && region->is_user_accessible() && region->is_stack(); 627} 628 629bool MemoryManager::validate_kernel_read(const Process& process, VirtualAddress vaddr, size_t size) const 630{ 631 return validate_range<AccessSpace::Kernel, AccessType::Read>(process, vaddr, size); 632} 633 634bool MemoryManager::can_read_without_faulting(const Process& process, VirtualAddress vaddr, size_t size) const 635{ 636 // FIXME: Use the size argument! 637 UNUSED_PARAM(size); 638 auto* pte = const_cast<MemoryManager*>(this)->pte(process.page_directory(), vaddr); 639 if (!pte) 640 return false; 641 return pte->is_present(); 642} 643 644 645bool MemoryManager::validate_user_read(const Process& process, VirtualAddress vaddr, size_t size) const 646{ 647 if (!is_user_address(vaddr)) 648 return false; 649 return validate_range<AccessSpace::User, AccessType::Read>(process, vaddr, size); 650} 651 652bool MemoryManager::validate_user_write(const Process& process, VirtualAddress vaddr, size_t size) const 653{ 654 if (!is_user_address(vaddr)) 655 return false; 656 return validate_range<AccessSpace::User, AccessType::Write>(process, vaddr, size); 657} 658 659void MemoryManager::register_vmobject(VMObject& vmobject) 660{ 661 InterruptDisabler disabler; 662 m_vmobjects.append(&vmobject); 663} 664 665void MemoryManager::unregister_vmobject(VMObject& vmobject) 666{ 667 InterruptDisabler disabler; 668 m_vmobjects.remove(&vmobject); 669} 670 671void MemoryManager::register_region(Region& region) 672{ 673 InterruptDisabler disabler; 674 if (region.vaddr().get() >= 0xc0000000) 675 m_kernel_regions.append(&region); 676 else 677 m_user_regions.append(&region); 678} 679 680void MemoryManager::unregister_region(Region& region) 681{ 682 InterruptDisabler disabler; 683 if (region.vaddr().get() >= 0xc0000000) 684 m_kernel_regions.remove(&region); 685 else 686 m_user_regions.remove(&region); 687} 688 689void MemoryManager::dump_kernel_regions() 690{ 691 kprintf("Kernel regions:\n"); 692 kprintf("BEGIN END SIZE ACCESS NAME\n"); 693 for (auto& region : MM.m_kernel_regions) { 694 kprintf("%08x -- %08x %08x %c%c%c%c%c%c %s\n", 695 region.vaddr().get(), 696 region.vaddr().offset(region.size() - 1).get(), 697 region.size(), 698 region.is_readable() ? 'R' : ' ', 699 region.is_writable() ? 'W' : ' ', 700 region.is_executable() ? 'X' : ' ', 701 region.is_shared() ? 'S' : ' ', 702 region.is_stack() ? 'T' : ' ', 703 region.vmobject().is_purgeable() ? 'P' : ' ', 704 region.name().characters()); 705 } 706} 707 708ProcessPagingScope::ProcessPagingScope(Process& process) 709{ 710 ASSERT(Thread::current); 711 m_previous_cr3 = read_cr3(); 712 MM.enter_process_paging_scope(process); 713} 714 715ProcessPagingScope::~ProcessPagingScope() 716{ 717 InterruptDisabler disabler; 718 Thread::current->tss().cr3 = m_previous_cr3; 719 write_cr3(m_previous_cr3); 720} 721 722}