Serenity Operating System
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 ®ion;
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 ®ion;
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(®ion);
676 else
677 m_user_regions.append(®ion);
678}
679
680void MemoryManager::unregister_region(Region& region)
681{
682 InterruptDisabler disabler;
683 if (region.vaddr().get() >= 0xc0000000)
684 m_kernel_regions.remove(®ion);
685 else
686 m_user_regions.remove(®ion);
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}