summaryrefslogtreecommitdiff
path: root/kernel/vm.c
blob: c1adbe7a71c4b5e5649d289ce2b83228a70095c9 (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
#include "kernel/vm.h"
#include "kernel/kalloc.h"
#include "kernel/lib.h"
#include "kernel/riscv.h"

enum
{
    VM_VALID    = 1,
    VM_USER     = 16,
    VM_GLOBAL   = 32,
    VM_ACCESSED = 64,
    VM_DIRTY    = 128,
};

struct PageTable
{
    uint32_t entries[1024];
};
typedef struct PageTable PageTable;
static_assert(sizeof(PageTable) == PAGE_SIZE);

static PageTable* kroot = nullptr;

static inline bool is_aligned(uint32_t p)
{
    return (p & (PAGE_SIZE - 1)) == 0;
}

static inline void* offset_page(void* p)
{
    return (void*)((char*)p + PAGE_SIZE);
}

void kvm_map_one(PageTable* root, void* va, void* pa, uint32_t mode)
{
    uint32_t vpn[] = {
        ((uint32_t)va >> 12) & 0x3ff,
        ((uint32_t)va >> 22) & 0x3ff,
    };

    uint32_t fl = root->entries[vpn[1]];
    if (!(fl & VM_VALID))
    {
        PageTable* child = (PageTable*)kzalloc();
        fl = (((uint32_t)child >> 2) & 0xffff'fc00) | VM_VALID;
        root->entries[vpn[1]] = fl;
    }

    PageTable* child = (PageTable*)((fl & 0xffff'fc00) << 2);
        
    uint32_t ppn[] = {
        ((uint32_t)pa >> 12) & 0x3ff,
        ((uint32_t)pa >> 22) & 0x3ff,
    };

    child->entries[vpn[0]] = (ppn[1] << 20) | (ppn[0] << 10) | VM_VALID | mode;
}

void kvm_map(void* va, void* pa, uint32_t size, uint32_t mode)
{
    if (!is_aligned((uint32_t)va)) panic("kvm_map: virtual address not aligned");
    if (!is_aligned((uint32_t)pa)) panic("kvm_map: physical address not aligned");
    if (!is_aligned(size)) panic("kvm_map: size not aligned");

    for (; size > 0; size -= PAGE_SIZE)
    {
        kvm_map_one(kroot, va, pa, mode);
        va = offset_page(va);
        pa = offset_page(pa);
    }
}

extern char _text_start;
extern char _text_end;

extern char _rodata_start;
extern char _rodata_end;

extern char _data_start;
extern char _data_end;

extern char _bss_start;
extern char _bss_end;

extern char _stack_start;
extern char _stack_end;

extern char _heap_start;
extern char _heap_end;

void kvm_init()
{
    kroot = (PageTable*)kzalloc();

    kvm_map(&_text_start, &_text_start, &_text_end - &_text_start, VM_R | VM_X);
    kvm_map(&_rodata_start, &_rodata_start, &_rodata_end - &_rodata_start, VM_R);
    kvm_map(&_data_start, &_data_start, &_data_end - &_data_start, VM_RW);
    kvm_map(&_bss_start, &_bss_start, &_bss_end - &_bss_start, VM_RW);
    kvm_map(&_heap_start, &_heap_start, &_heap_end - &_heap_start, VM_RW);
    kvm_map(&_stack_start, &_stack_start, &_stack_end - &_stack_start, VM_RW);
    
    kvm_map((void*)0x1000'0000, (void*)0x1000'0000, PAGE_SIZE, VM_RW); // UART
    kvm_map((void*)0x0c00'0000, (void*)0x0c00'0000, 0x8000, VM_RW); // APLIC-M
    kvm_map((void*)0x0d00'0000, (void*)0x0d00'0000, 0x8000, VM_RW); // APLIC-S

    w_satp(SATP_MODE_SV32 | ((uint32_t)kroot >> 12));
    sfence_vma();
}