2f8d726ef86c669c23d100ac729f7765c1b00211
[lunaix-os.git] / lunaix-os / kernel / mm / vmm.c
1 #include <libc/string.h>
2 #include <lunaix/mm/page.h>
3 #include <lunaix/mm/pmm.h>
4 #include <lunaix/mm/vmm.h>
5 #include <lunaix/assert.h>
6 #include <hal/cpu.h>
7
8 void
9 vmm_init()
10 {
11     // TODO: something here?
12 }
13
14 ptd_t*
15 vmm_init_pd()
16 {
17     ptd_t* dir = pmm_alloc_page();
18     for (size_t i = 0; i < PG_MAX_ENTRIES; i++) {
19         dir[i] = 0;
20     }
21
22     // 递归映射,方便我们在软件层面进行查表地址转换
23     dir[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, dir);
24
25     return dir;
26 }
27
28 int
29 __vmm_map_internal(uint32_t l1_inx, uint32_t l2_inx, uintptr_t pa, pt_attr attr) {
30     ptd_t* l1pt = (ptd_t*)L1_BASE_VADDR;
31     pt_t* l2pt = (pt_t*)L2_VADDR(l1_inx);
32
33     // See if attr make sense
34     assert(attr <= 128);
35
36     if(!l1pt[l1_inx]) {
37         uint8_t* new_l1pt_pa = pmm_alloc_page();
38
39         // 物理内存已满!
40         if (!new_l1pt_pa) {
41             return 0;
42         }
43
44         l1pt[l1_inx] = NEW_L1_ENTRY(attr, new_l1pt_pa);
45         memset((void*)L2_VADDR(l1_inx), 0, PM_PAGE_SIZE);
46     }
47
48     l2pt[l2_inx] = NEW_L2_ENTRY(attr, pa);
49
50     return 1;
51 }
52
53 void*
54 vmm_map_page(void* va, void* pa, pt_attr tattr)
55 {
56     // 显然,对空指针进行映射没有意义。
57     if (!pa || !va) {
58         return NULL;
59     }
60
61     assert(((uintptr_t)va & 0xFFFU) == 0)
62     assert(((uintptr_t)pa & 0xFFFU) == 0)
63
64     uint32_t l1_index = L1_INDEX(va);
65     uint32_t l2_index = L2_INDEX(va);
66     ptd_t* l1pt = (ptd_t*)L1_BASE_VADDR;
67
68     // 在页表与页目录中找到一个可用的空位进行映射(位于va或其附近)
69     ptd_t l1pte = l1pt[l1_index];
70     pt_t* l2pt = (pt_t*)L2_VADDR(l1_index);
71     while (l1pte && l1_index < PG_MAX_ENTRIES) {
72         if (l2_index == PG_MAX_ENTRIES) {
73             l1_index++;
74             l2_index = 0;
75             l1pte = l1pt[l1_index];
76             l2pt = (pt_t*)L2_VADDR(l1_index);
77         }
78         // 页表有空位,只需要开辟一个新的 PTE (Level 2)
79         if (l2pt && !l2pt[l2_index]) {
80             l2pt[l2_index] = NEW_L2_ENTRY(tattr, pa);
81             return (void*)V_ADDR(l1_index, l2_index, PG_OFFSET(va));
82         }
83         l2_index++;
84     }
85
86     // 页目录与所有页表已满!
87     if (l1_index > PG_MAX_ENTRIES) {
88         return NULL;
89     }
90
91     if (!__vmm_map_internal(l1_index, l2_index, pa, tattr)) {
92         return NULL;
93     }
94
95     return (void*)V_ADDR(l1_index, l2_index, PG_OFFSET(va));
96 }
97
98 void*
99 vmm_fmap_page(void* va, void* pa, pt_attr tattr) {
100     if (!pa || !va) {
101         return NULL;
102     }
103
104     assert(((uintptr_t)va & 0xFFFU) == 0)
105     assert(((uintptr_t)pa & 0xFFFU) == 0)
106
107     uint32_t l1_index = L1_INDEX(va);
108     uint32_t l2_index = L2_INDEX(va);
109
110     if (!__vmm_map_internal(l1_index, l2_index, pa, tattr)) {
111         return NULL;
112     }
113
114     cpu_invplg(va);
115
116     return (void*)V_ADDR(l1_index, l2_index, PG_OFFSET(va));
117 }
118
119 void*
120 vmm_alloc_page(void* vpn, pt_attr tattr)
121 {
122     void* pp = pmm_alloc_page();
123     void* result = vmm_map_page(vpn, pp, tattr);
124     if (!result) {
125         pmm_free_page(pp);
126     }
127     return result;
128 }
129
130 void
131 vmm_unmap_page(void* va)
132 {
133     assert(((uintptr_t)va & 0xFFFU) == 0)
134
135     uint32_t l1_index = L1_INDEX(va);
136     uint32_t l2_index = L2_INDEX(va);
137     ptd_t* l1pt = (ptd_t*)L1_BASE_VADDR;
138
139     ptd_t l1pte = l1pt[l1_index];
140
141     if (l1pte) {
142         pt_t* l2pt = (pt_t*)L2_VADDR(l1_index);
143         uint32_t l2pte = l2pt[l2_index];
144         if (IS_CACHED(l2pte) && pmm_free_page((void*)l2pte)) {
145             cpu_invplg(va);
146         }
147         l2pt[l2_index] = 0;
148     }
149 }
150
151 v_mapping
152 vmm_lookup(void* va)
153 {
154     assert(((uintptr_t)va & 0xFFFU) == 0)
155
156     uint32_t l1_index = L1_INDEX(va);
157     uint32_t l2_index = L2_INDEX(va);
158     uint32_t po = PG_OFFSET(va);
159     ptd_t* l1pt = (ptd_t*)L1_BASE_VADDR;
160
161     ptd_t l1pte = l1pt[l1_index];
162
163     v_mapping mapping = { .flags = 0, .pa = 0, .pn = 0 };
164     if (l1pte) {
165         pt_t l2pte = ((pt_t*)L2_VADDR(l1_index))[l2_index];
166         if (l2pte) {
167             uintptr_t ppn = l2pte >> PG_SIZE_BITS;
168             mapping.flags = PG_ENTRY_FLAGS(l2pte);
169             mapping.pa = PG_ENTRY_ADDR(l2pte);
170             mapping.pn = mapping.pa >> PG_SIZE_BITS;
171         }
172     }
173
174     return mapping;
175 }
176
177 void*
178 vmm_v2p(void* va) {
179     return vmm_lookup(va).pa;
180 }