summaryrefslogtreecommitdiff
path: root/Kernel/VM/PageDirectory.cpp
blob: b8bbbdbe9b9ae12a86bff4d1ba628e8ac68e24d8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <AK/Memory.h>
#include <AK/Singleton.h>
#include <Kernel/Process.h>
#include <Kernel/Random.h>
#include <Kernel/VM/MemoryManager.h>
#include <Kernel/VM/PageDirectory.h>

namespace Kernel {

static const FlatPtr userspace_range_base = 0x00800000;
static const FlatPtr userspace_range_ceiling = 0xbe000000;

static AK::Singleton<HashMap<u32, PageDirectory*>> s_cr3_map;

static HashMap<u32, PageDirectory*>& cr3_map()
{
    VERIFY_INTERRUPTS_DISABLED();
    return *s_cr3_map;
}

RefPtr<PageDirectory> PageDirectory::find_by_cr3(u32 cr3)
{
    ScopedSpinLock lock(s_mm_lock);
    return cr3_map().get(cr3).value_or({});
}

extern "C" PageDirectoryEntry* boot_pdpt[4];
extern "C" PageDirectoryEntry boot_pd0[1024];
extern "C" PageDirectoryEntry boot_pd3[1024];

UNMAP_AFTER_INIT PageDirectory::PageDirectory()
{
    m_range_allocator.initialize_with_range(VirtualAddress(0xc2000000), 0x2f000000);
    m_identity_range_allocator.initialize_with_range(VirtualAddress(FlatPtr(0x00000000)), 0x00200000);

    // Adopt the page tables already set up by boot.S
    PhysicalAddress boot_pdpt_paddr(virtual_to_low_physical((FlatPtr)boot_pdpt));
    PhysicalAddress boot_pd0_paddr(virtual_to_low_physical((FlatPtr)boot_pd0));
    PhysicalAddress boot_pd3_paddr(virtual_to_low_physical((FlatPtr)boot_pd3));
    dmesgln("MM: boot_pdpt @ {}", boot_pdpt_paddr);
    dmesgln("MM: boot_pd0 @ {}", boot_pd0_paddr);
    dmesgln("MM: boot_pd3 @ {}", boot_pd3_paddr);
    m_directory_table = PhysicalPage::create(boot_pdpt_paddr, true, false);
    m_directory_pages[0] = PhysicalPage::create(boot_pd0_paddr, true, false);
    m_directory_pages[3] = PhysicalPage::create(boot_pd3_paddr, true, false);
}

PageDirectory::PageDirectory(const RangeAllocator* parent_range_allocator)
{
    ScopedSpinLock lock(s_mm_lock);
    if (parent_range_allocator) {
        m_range_allocator.initialize_from_parent(*parent_range_allocator);
    } else {
        size_t random_offset = (get_fast_random<u8>() % 32 * MiB) & PAGE_MASK;
        u32 base = userspace_range_base + random_offset;
        m_range_allocator.initialize_with_range(VirtualAddress(base), userspace_range_ceiling - base);
    }

    // Set up a userspace page directory
    m_directory_table = MM.allocate_user_physical_page();
    if (!m_directory_table)
        return;
    m_directory_pages[0] = MM.allocate_user_physical_page();
    if (!m_directory_pages[0])
        return;
    m_directory_pages[1] = MM.allocate_user_physical_page();
    if (!m_directory_pages[1])
        return;
    m_directory_pages[2] = MM.allocate_user_physical_page();
    if (!m_directory_pages[2])
        return;
    // Share the top 1 GiB of kernel-only mappings (>=3GiB or >=0xc0000000)
    m_directory_pages[3] = MM.kernel_page_directory().m_directory_pages[3];

    {
        auto& table = *(PageDirectoryPointerTable*)MM.quickmap_page(*m_directory_table);
        table.raw[0] = (FlatPtr)m_directory_pages[0]->paddr().as_ptr() | 1;
        table.raw[1] = (FlatPtr)m_directory_pages[1]->paddr().as_ptr() | 1;
        table.raw[2] = (FlatPtr)m_directory_pages[2]->paddr().as_ptr() | 1;
        table.raw[3] = (FlatPtr)m_directory_pages[3]->paddr().as_ptr() | 1;

        // 2 ** MAXPHYADDR - 1
        // Where MAXPHYADDR = physical_address_bit_width
        u64 max_physical_address = (1ULL << Processor::current().physical_address_bit_width()) - 1;

        // bit 63 = no execute
        // bit 7 = page size
        // bit 5 = accessed
        // bit 4 = cache disable
        // bit 3 = write through
        // bit 2 = user/supervisor
        // bit 1 = read/write
        // bit 0 = present
        constexpr u64 pdpte_bit_flags = 0x80000000000000BF;

        // This is to notify us of bugs where we're:
        // 1. Going over what the processor is capable of.
        // 2. Writing into the reserved bits (51:MAXPHYADDR), where doing so throws a GPF
        //    when writing out the PDPT pointer to CR3.
        // The reason we're not checking the page directory's physical address directly is because
        // we're checking for sign extension when putting it into a PDPTE. See issue #4584.
        VERIFY((table.raw[0] & ~pdpte_bit_flags) <= max_physical_address);
        VERIFY((table.raw[1] & ~pdpte_bit_flags) <= max_physical_address);
        VERIFY((table.raw[2] & ~pdpte_bit_flags) <= max_physical_address);
        VERIFY((table.raw[3] & ~pdpte_bit_flags) <= max_physical_address);

        MM.unquickmap_page();
    }

    // Clone bottom 2 MiB of mappings from kernel_page_directory
    PageDirectoryEntry buffer;
    auto* kernel_pd = MM.quickmap_pd(MM.kernel_page_directory(), 0);
    memcpy(&buffer, kernel_pd, sizeof(PageDirectoryEntry));
    auto* new_pd = MM.quickmap_pd(*this, 0);
    memcpy(new_pd, &buffer, sizeof(PageDirectoryEntry));

    // If we got here, we successfully created it. Set m_space now
    m_valid = true;

    cr3_map().set(cr3(), this);
}

PageDirectory::~PageDirectory()
{
    ScopedSpinLock lock(s_mm_lock);
    if (m_space)
        cr3_map().remove(cr3());
}

}