summaryrefslogtreecommitdiff
path: root/arch/i386/kernel/paging.c
blob: 54539ac875db2e875dce89d33ef3332ec48ac45f (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
#include <kernel/paging.h>
#include <kernel/io.h>
#include <kernel/syscall.h>
#include <kernel/string.h>

extern uintptr_t _kernel_start;
extern uintptr_t _kernel_end;
static uintptr_t kstart = (uintptr_t)&_kernel_start;
static uintptr_t kend = (uintptr_t)&_kernel_end - 0xC0000000;

static uintptr_t page_directory[1024] __attribute__((aligned(PAGE_SIZE)));
static uintptr_t first_page_table[1024] __attribute__((aligned(PAGE_SIZE)));

static int paging_enabled = 0;

static uint32_t dma_zone_bitmap[DMA_BITMAP_SZ];
static uint32_t buddy_bitmap[BDY_BITMAP_SZ];
static struct pfa_buddy first_buddy;

static void _pfa_init(void) {
        first_buddy.start = 0x01000000;
        first_buddy.bitmap = buddy_bitmap;
}

static void _pfa_alloc(uintptr_t paddr) {
        uintptr_t index = (paddr & 0xFFFFF000) / 4096 / 32;
        uintptr_t bit = (paddr & 0xFFFFF000) / 4096 % 32;
        uint32_t *bitmap;
        if (paddr < 0x01000000)
                bitmap = dma_zone_bitmap;
        else
                bitmap = first_buddy.bitmap;

        bitmap[index] |= (1 << bit);
}

static void _pfa_free(uintptr_t paddr) {
        uintptr_t index = (paddr & 0xFFFFF000) / 4096 / 32;
        uintptr_t bit = (paddr & 0xFFFFF000) / 4096 % 32;
        uint32_t *bitmap;
        if (paddr < 0x01000000)
                bitmap = dma_zone_bitmap;
        else
                bitmap = first_buddy.bitmap;

        bitmap[index] &= ~(1 << bit);
}

void paging_init(void) {
        _pfa_init();
        for (int i = 0; i < 1024; i++)
                page_directory[i] = 0x00000002;

        page_directory[1023] = ((uintptr_t)&page_directory - 0xC0000000) | 3;
        page_directory[0] = ((uintptr_t)&first_page_table - 0xC0000000) | 3;
        page_directory[768] = ((uintptr_t)&first_page_table - 0xC0000000) | 3;
        for (uintptr_t i = kstart; i < kend; i += 4096) {
                _pfa_alloc(get_vaddr(i));
                map_page(page_directory, i, get_vaddr(i), 0x003);
        }

        load_page_dir(((uintptr_t)&page_directory) - 0xC0000000);
        enable_paging();
        paging_enabled = 1;
        return;
}

void page_fault_handler(struct isr_frame *frame) {
        uintptr_t errno = frame->errno;

        uintptr_t fault_addr;
        __asm__ volatile("movl %%cr2, %0" : "=r"(fault_addr));
        int present = errno & ERR_PRESENT;
        int rw = errno & ERR_RW;
        int user = errno & ERR_USER;
        int reserved = errno & ERR_RESERVED;
        int ifetch = errno & ERR_INST;

        if (!present)
                _pfa_alloc(fault_addr);
                map_page(NULL, fault_addr, fault_addr, 0x003);
        if (user)
                panic("Usermode attempted to read supervisor page");
        if (rw)
                panic("Task wrote to read only page");
        if (reserved)
                panic("Task overwrote reserved page bits");
        if (ifetch)
                panic("Task paging instruction fetch failure");
}

uintptr_t get_vaddr(uintptr_t paddr) {
        return paddr + 0xC0000000;
}

void map_page(uint32_t *pd, uintptr_t paddr, uintptr_t vaddr, uint32_t flags) {
        if (pd == NULL)
                pd = page_directory;

        uintptr_t paddr_aligned = paddr & 0xFFFFF000;
        uintptr_t vaddr_aligned = vaddr & 0xFFFFF000;

        uintptr_t pdindex = vaddr_aligned >> 22;
        uintptr_t ptindex = vaddr_aligned >> 12 & 0x03FF;

        uintptr_t *pt = (pd[pdindex] + 0xC0000000 & 0xFFFFF000);
        uintptr_t *pte = (uintptr_t*)(&pt[ptindex]);
        *pte |= paddr_aligned | (flags & 0xFFF) | 0x01;
}

void unmap_page(uint32_t *pd, uintptr_t vaddr) {
        if (pd == NULL)
                pd = page_directory;

        uintptr_t pdindex = vaddr >> 22;
        uintptr_t ptindex = vaddr >> 12 & 0x03FF;

        uintptr_t *pt = (pd[pdindex] + 0xC0000000 & 0xFFFFF000);
        uintptr_t *pte = (uintptr_t*)(&pt[ptindex]);
        *pte &= 0;
}