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