summaryrefslogtreecommitdiffstats
path: root/arch/x86_64/hal/paging.c
blob: 7b450058734685806fc4c5ee436367dae0525a6e (plain) (blame)
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
#include "paging.h"
#include "arch/x86_64/x86_64.h"
#include "jove.h"
#include "init.h"
#include "config.h"
#include "hal/obj.h"
#include "lib/string.h"
#include "lib/printf.h"
#include "lib/panic.h"
#include "idt.h"
#include "api/syscall.h"

PAGEALIGN page_t _kernel_pml4[512];
PAGEALIGN page_t _kernel_pml3[512];
PAGEALIGN page_t _kernel_pml2[512];
PAGEALIGN page_t _kernel_pml1[512];

#define IDENT_GIANTPAGES ((PHYS_HUGEPAGES_MAX + 511) / 512)
#define IDENT_MASSIVEPAGES ((IDENT_GIANTPAGES + 511) / 512)

PAGEALIGN static page_t ident_pml3[IDENT_MASSIVEPAGES][512];
PAGEALIGN static page_t ident_pml2[IDENT_GIANTPAGES][512];

void*
phys_tovirt(uintptr_t phys)
{
    return (void*)(phys | IDENT_VIRT_BASE);
}

uintptr_t
paging_get_pml4(void)
{
    uintptr_t ret = 0;
    __asm__ volatile("movq %%cr3, %0": "=r"(ret));
    return ret;
}

void
paging_pml4_switch(uintptr_t pml4)
{
    __asm__ volatile("movq %0, %%cr3":: "r"(pml4));
}

static struct int_stackframe*
s_int_handle_pf_early(struct int_stackframe *frame)
{
    extern int _kernel_panic_unlock;
    _kernel_panic_unlock = 1;

    union {
        struct {
            uint8_t p : 1;
            uint8_t wr : 1;
            uint8_t us : 1;
            uint8_t rsvd : 1;
            uint8_t id : 1;
            uint8_t pk : 1;
        };
        uint64_t value;
    } pf_error = { .value = frame->int_err };
    
    kprintf("Kernel has encountered a page fault exception early in initializaiton\n");
    kprintf("%s %s during %s\n", 
            pf_error.wr ? "Wrote to" : "Read from",
            pf_error.p ? "Present" : "Non-present",
            pf_error.id ? "Instruction fetch" : "Data access");
    uintptr_t cr2 = 0;
    __asm__ volatile("movq %%cr2, %0": "=r"(cr2));
    kprintf("Faulting address: %p\n", cr2);
    kprintf("RIP: %p | RSP: %p\n", frame->int_rip, frame->int_rsp);
    print_state(&frame->state);

    hcf();
}

int pagedir_map_frame(
        jove_owned_obj_t *tgt, jove_owned_obj_t *untyped_obj, 
        uintptr_t vptr, struct syscall_pagedir_flags flg)
{
    page_t *pml4 = obj_deref(tgt);
    uintptr_t frame = obj_getvalue(untyped_obj);

    size_t pml4i = PML4I(vptr);
    if(!pml4[pml4i].p) return ENOMAP;

    page_t *pml3 = (page_t*)(((uintptr_t)pml4[pml4i].paddr << 12) | IDENT_VIRT_BASE);
    size_t pml3i = PML3I(vptr);
    if(!pml3[pml3i].p) return ENOMAP;

    page_t *pml2 = (page_t*)(((uintptr_t)pml3[pml3i].paddr << 12) | IDENT_VIRT_BASE);
    size_t pml2i = PML2I(vptr);
    if(!pml2[pml2i].p) return ENOMAP;

    page_t *pml1 = (page_t*)(((uintptr_t)pml2[pml2i].paddr << 12) | IDENT_VIRT_BASE);
    size_t pml1i = PML1I(vptr);
    if(pml1[pml1i].p) return EEXIST;

    pml1[pml1i].value = frame |
        PAGE_PRESENT |
        PAGE_USER |
        (flg.writeable ? PAGE_WRITETHROUGH : 0) |
        (flg.executable ? 0 : PAGE_XD);
    return 0;
}

int pagedir_map_table(
        jove_owned_obj_t *tgt, jove_owned_obj_t *untyped_obj, 
        uintptr_t vptr, struct syscall_pagedir_flags flg)
{
    page_t *target_table = NULL;
    page_t *pml4 = obj_deref(tgt);
    uintptr_t frame = obj_getvalue(untyped_obj);

    void *frame_ident = obj_deref(untyped_obj);
    memset(frame_ident, 0, 0x1000);

    size_t pml4i = PML4I(vptr);
    if(!pml4[pml4i].p) {
        target_table = &pml4[pml4i];
        goto map_table;
    }

    page_t *pml3 = (page_t*)(((uintptr_t)pml4[pml4i].paddr << 12) | IDENT_VIRT_BASE);
    size_t pml3i = PML3I(vptr);
    if(!pml3[pml3i].p) {
        target_table = &pml3[pml3i];
        goto map_table;
    }

    page_t *pml2 = (page_t*)(((uintptr_t)pml3[pml3i].paddr << 12) | IDENT_VIRT_BASE);
    size_t pml2i = PML2I(vptr);
    if(!pml2[pml2i].p) {
        target_table = &pml2[pml2i];
        goto map_table;
    }
    return EHASMAP;

map_table:
    target_table->value = frame |
        PAGE_PRESENT |
        PAGE_USER |
        (flg.writeable ? PAGE_WRITETHROUGH : 0) |
        (flg.executable ? 0 : PAGE_XD);
    return 0;
}


void
paging_pml4_init(void)
{
    //Setup kernel pages
    memset(_kernel_pml4, 0, 0x1000);
    memset(_kernel_pml3, 0, 0x1000);
    memset(_kernel_pml2, 0, 0x1000);
    memset(_kernel_pml1, 0, 0x1000);

    memset(ident_pml3, 0, 0x1000 * IDENT_MASSIVEPAGES);
    memset(ident_pml2, 0, 0x1000 * IDENT_GIANTPAGES);

    /*Paging entries depend on physical addresses to properly map.
     * Since the virtual kernel address differs from the physical memory address,
     * we need to convert the pointers using the physical memory pointer passed
     * to us by the bootloader.*/
    uintptr_t kernel_pml4_pptr = KPTR_TOPHYS((uintptr_t)_kernel_pml4);
    uintptr_t kernel_pml3_pptr = KPTR_TOPHYS((uintptr_t)_kernel_pml3);
    uintptr_t kernel_pml2_pptr = KPTR_TOPHYS((uintptr_t)_kernel_pml2);
    uintptr_t kernel_pml1_pptr = KPTR_TOPHYS((uintptr_t)_kernel_pml1);

    /*Maps pml4-2 as global writeable pages.*/
    _kernel_pml4[511].value = kernel_pml3_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_GLOBAL;
    _kernel_pml3[510].value = kernel_pml2_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_GLOBAL;
    _kernel_pml2[0].value =   kernel_pml1_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_GLOBAL;

    /*text*/
    extern void *_kernel_text_start;
    extern void *_kernel_text_end;
    uintptr_t kernel_text_start_pptr = KPTR_TOPHYS((uintptr_t)&_kernel_text_start);
    uintptr_t kernel_text_end_pptr = KPTR_TOPHYS((uintptr_t)&_kernel_text_end);
    size_t kernel_text_pages = ((kernel_text_end_pptr - kernel_text_start_pptr) / 0x1000) + 1;
    
    uintptr_t kernel_text_off = (((uintptr_t)&_kernel_text_start) - KERNEL_VIRT_BASE) >> 12;

    for(size_t i = 0; i < kernel_text_pages; i++) {
        size_t off = i + kernel_text_off;
        uintptr_t page_off = kernel_text_start_pptr + (i << 12);
        _kernel_pml1[off].value = page_off | PAGE_PRESENT | PAGE_GLOBAL;
    }

    /*rodata*/
    extern void *_kernel_rodata_start;
    extern void *_kernel_rodata_end;
    uintptr_t kernel_rodata_start_pptr = KPTR_TOPHYS((uintptr_t)&_kernel_rodata_start);
    uintptr_t kernel_rodata_end_pptr = KPTR_TOPHYS((uintptr_t)&_kernel_rodata_end);
    size_t kernel_rodata_pages = ((kernel_rodata_end_pptr - kernel_rodata_start_pptr) / 0x1000) + 1;

    uintptr_t kernel_rodata_off = kernel_text_off + kernel_text_pages;
    for(size_t i = 0; i < kernel_rodata_pages; i++) {
        size_t off = i + kernel_rodata_off;
        uintptr_t page_off = kernel_text_start_pptr + (off << 12);
        _kernel_pml1[off].value = page_off | PAGE_PRESENT | PAGE_GLOBAL | PAGE_XD;
    }

    /*data/bss*/
    extern void *_kernel_data_start;
    extern void *_kernel_bss_end;
    uintptr_t kernel_data_start_pptr = KPTR_TOPHYS((uintptr_t)&_kernel_data_start);
    uintptr_t kernel_data_end_pptr = KPTR_TOPHYS((uintptr_t)&_kernel_bss_end);
    size_t kernel_data_pages = ((kernel_data_end_pptr - kernel_data_start_pptr) / 0x1000) + 1;

    uintptr_t kernel_data_off = kernel_rodata_off + kernel_rodata_pages;
    for(size_t i = 0; i < kernel_data_pages; i++) {
        size_t off = i + kernel_data_off;
        uintptr_t page_off = kernel_text_start_pptr + (off << 12);
        _kernel_pml1[off].value = page_off | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_GLOBAL | PAGE_XD;
    }

    /*Identity pages are mapped as huge pages starting at the 256'th PML3 entry.
     *We want to support any range of physical memory up to several TiB, so
     *the mapper has to consider multiple heirarchies of huge pages.*/

    for(size_t l3 = 0; l3 < IDENT_MASSIVEPAGES; l3++) {
        uintptr_t ident_pml3_pptr = KPTR_TOPHYS(&ident_pml3[l3]);

        _kernel_pml4[256 + l3].value = ident_pml3_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_GLOBAL;
    }

    for(size_t l2 = 0; l2 < IDENT_GIANTPAGES; l2++) {
        size_t l3 = l2 / 512;
        size_t l2_mod = l2 % 512;

        uintptr_t ident_pml2_pptr = KPTR_TOPHYS(&ident_pml2[l2]);
        ident_pml3[l3][l2_mod].value = ident_pml2_pptr | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_GLOBAL;
    }

    for(size_t l1 = 0; l1 < PHYS_HUGEPAGES_MAX; l1++) {
        size_t l2 = l1 / 512;
        size_t l1_mod = l1 % 512;

        ident_pml2[l2][l1_mod].value = (l1 << 21) | PAGE_PRESENT | PAGE_WRITEABLE | PAGE_PS | PAGE_GLOBAL | PAGE_XD;
        ident_pml2[l2][l1_mod].xd = 1;
    }

    paging_pml4_switch(kernel_pml4_pptr);

    {
        jove_owned_obj_t *pgdir_o = objdir_getlocal(_rootdir_obj, ROOTDIR_PAGEDIR_I);
        obj_settype(pgdir_o, JOVE_OBJ_PAGEDIR);
        obj_setvalue(pgdir_o, kernel_pml4_pptr);
        obj_release(pgdir_o);
    }

    /*Before tasking, if the kernel has a bug and crashes we need some
     * information about what happened.*/
    extern struct int_stackframe *(*__int_kernel_handles[])(struct int_stackframe*);
    __int_kernel_handles[IDT_EXCEPT_PF] = s_int_handle_pf_early;
}