summaryrefslogtreecommitdiff
path: root/src/kernel/mm/vm.cc
blob: 8696fdbb2b0aa079c0326b30b6371379dc02b9b8 (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
#include "kernel/mm.h"
#include "kernel/pmm.h"
#include "drivers/uart.h"
#include "common/types.h"

extern "C" char text_start[];
extern "C" char text_end[];
extern "C" char rodata_start[];
extern "C" char rodata_end[];
extern "C" char data_start[];
extern "C" char kernel_end[];

namespace VM
{
    pagetable_t kernel_pagetable;

    #define PX(level, va) ((((uint64)va) >> (12 + 9 * (level))) & 0x1FF)
    #define PA2PTE(pa) ((((uint64)pa) >> 12) << 10)
    #define PTE2PA(pte) (((pte) >> 10) << 12)

    pte_t *walk(pagetable_t pagetable, uint64 va, int alloc)
    {
        for (int level = 2; level > 0; level--)
        {
            pte_t *pte = &pagetable[PX(level, va)];

            if (*pte & PTE_V)
            {
                pagetable = (pagetable_t)PTE2PA(*pte);
            }
            else
            {
                if (!alloc)
                    return 0;

                pagetable = (pagetable_t)PMM::alloc_page();
                if (!pagetable)
                    return 0;

                *pte = PA2PTE(pagetable) | PTE_V;
            }
        }
        return &pagetable[PX(0, va)];
    }

    int mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm)
    {
        uint64 a, last;
        pte_t *pte;

        a = (va / PGSIZE) * PGSIZE;
        last = ((va + size - 1) / PGSIZE) * PGSIZE;

        for(;;)
        {
            if((pte = walk(pagetable, a, 1)) == 0)
                return -1;

            if (*pte & PTE_V)
            {
                Drivers::uart_puts("Remap error\n");
                return -1;
            }

            *pte = PA2PTE(pa) | perm | PTE_V;

            if (a == last)
                break;
            a += PGSIZE;
            pa += PGSIZE;
        }
        return 0;
    }

    void kvminit()
    {
        kernel_pagetable = (pagetable_t)PMM::alloc_page();

        mappages(kernel_pagetable, 0x10000000, 0x10000, 0x10000000, PTE_R | PTE_W);

        mappages(kernel_pagetable, 0x2000000, 0x10000, 0x2000000, PTE_R | PTE_W);

        mappages(kernel_pagetable, (uint64)text_start, (uint64)text_end - (uint64)text_start,
                 (uint64)text_start, PTE_R | PTE_X);

        mappages(kernel_pagetable, (uint64)data_start, PHYSTOP - (uint64)data_start,
                 (uint64)data_start, PTE_R | PTE_W);

        mappages(kernel_pagetable, (uint64)rodata_start, (uint64)rodata_end - (uint64)rodata_start,
                 (uint64)rodata_start, PTE_R);
        
    }
    void kvminithart()
    {
        asm volatile("sfence.vma zero, zero");

        uint64 satp_val = MAKE_SATP(kernel_pagetable);
        asm volatile("csrw satp, %0" : : "r"(satp_val));

        asm volatile("sfence.vma zero, zero");
    }
}