basic process support and some syscalls
[lunaix-os.git] / lunaix-os / kernel / mm / vmm.c
1 #include <hal/cpu.h>
2 #include <klibc/string.h>
3 #include <lunaix/mm/vmm.h>
4 #include <lunaix/spike.h>
5
6 #include <stdbool.h>
7
8 void
9 vmm_init()
10 {
11     // XXX: something here?
12 }
13
14 x86_page_table*
15 vmm_init_pd()
16 {
17     x86_page_table* dir = (x86_page_table*)pmm_alloc_page(KERNEL_PID, PP_FGPERSIST);
18     for (size_t i = 0; i < PG_MAX_ENTRIES; i++) {
19         dir->entry[i] = PTE_NULL;
20     }
21
22     // 递归映射,方便我们在软件层面进行查表地址转换
23     dir->entry[PG_MAX_ENTRIES - 1] = NEW_L1_ENTRY(T_SELF_REF_PERM, dir);
24
25     return dir;
26 }
27
28 int
29 __vmm_map_internal(pid_t pid, 
30                    uint32_t l1_inx,
31                    uint32_t l2_inx,
32                    uintptr_t pa,
33                    pt_attr attr,
34                    int forced)
35 {
36     x86_page_table* l1pt = (x86_page_table*)L1_BASE_VADDR;
37     x86_page_table* l2pt = (x86_page_table*)L2_VADDR(l1_inx);
38
39     // See if attr make sense
40     assert(attr <= 128);
41
42     if (!l1pt->entry[l1_inx]) {
43         x86_page_table* new_l1pt_pa = pmm_alloc_page(pid, PP_FGPERSIST);
44
45         // 物理内存已满!
46         if (!new_l1pt_pa) {
47             return 0;
48         }
49
50         // This must be writable
51         l1pt->entry[l1_inx] = NEW_L1_ENTRY(attr | PG_WRITE, new_l1pt_pa);
52         memset((void*)L2_VADDR(l1_inx), 0, PG_SIZE);
53     }
54
55     x86_pte_t l2pte = l2pt->entry[l2_inx];
56     if (l2pte) {
57         if (!forced) {
58             return 0;
59         }
60     }
61
62     if ((HAS_FLAGS(attr, PG_PRESENT))) {
63         // add one on reference count, regardless of existence.
64         pmm_ref_page(pid, pa);
65     }
66
67     l2pt->entry[l2_inx] = NEW_L2_ENTRY(attr, pa);
68
69     return 1;
70 }
71
72 void*
73 vmm_map_page(pid_t pid, void* va, void* pa, pt_attr tattr)
74 {
75     // 显然,对空指针进行映射没有意义。
76     if (!pa || !va) {
77         return NULL;
78     }
79
80     assert(((uintptr_t)va & 0xFFFU) == 0) assert(((uintptr_t)pa & 0xFFFU) == 0);
81
82     uint32_t l1_index = L1_INDEX(va);
83     uint32_t l2_index = L2_INDEX(va);
84     x86_page_table* l1pt = (x86_page_table*)L1_BASE_VADDR;
85
86     // 在页表与页目录中找到一个可用的空位进行映射(位于va或其附近)
87     x86_pte_t l1pte = l1pt->entry[l1_index];
88     x86_page_table* l2pt = (x86_page_table*)L2_VADDR(l1_index);
89     while (l1pte && l1_index < PG_MAX_ENTRIES) {
90         if (l2_index == PG_MAX_ENTRIES) {
91             l1_index++;
92             l2_index = 0;
93             l1pte = l1pt->entry[l1_index];
94             l2pt = (x86_page_table*)L2_VADDR(l1_index);
95         }
96         // 页表有空位,只需要开辟一个新的 PTE (Level 2)
97         if (__vmm_map_internal(pid, l1_index, l2_index, pa, tattr, false)) {
98             return (void*)V_ADDR(l1_index, l2_index, PG_OFFSET(va));
99         }
100         l2_index++;
101     }
102
103     // 页目录与所有页表已满!
104     if (l1_index > PG_MAX_ENTRIES) {
105         return NULL;
106     }
107
108     if (!__vmm_map_internal(pid, l1_index, l2_index, (uintptr_t)pa, tattr, false)) {
109         return NULL;
110     }
111
112     return (void*)V_ADDR(l1_index, l2_index, PG_OFFSET(va));
113 }
114
115 void*
116 vmm_fmap_page(pid_t pid, void* va, void* pa, pt_attr tattr)
117 {
118     if (!pa || !va) {
119         return NULL;
120     }
121
122     assert(((uintptr_t)va & 0xFFFU) == 0) assert(((uintptr_t)pa & 0xFFFU) == 0);
123
124     uint32_t l1_index = L1_INDEX(va);
125     uint32_t l2_index = L2_INDEX(va);
126
127     if (!__vmm_map_internal(pid, l1_index, l2_index, (uintptr_t)pa, tattr, true)) {
128         return NULL;
129     }
130
131     cpu_invplg(va);
132
133     return va;
134 }
135
136 void*
137 vmm_alloc_page(pid_t pid, void* vpn, void** pa, pt_attr tattr, pp_attr_t pattr)
138 {
139     void* pp = pmm_alloc_page(pid, pattr);
140     void* result = vmm_map_page(pid, vpn, pp, tattr);
141     if (!result) {
142         pmm_free_page(pp, pid);
143     }
144     pa ? (*pa = pp) : 0;
145     return result;
146 }
147
148 int
149 vmm_alloc_pages(pid_t pid, void* va, size_t sz, pt_attr tattr, pp_attr_t pattr)
150 {
151     assert((uintptr_t)va % PG_SIZE == 0) assert(sz % PG_SIZE == 0);
152
153     void* va_ = va;
154     for (size_t i = 0; i < (sz >> PG_SIZE_BITS); i++, va_ += PG_SIZE) {
155         void* pp = pmm_alloc_page(pid, pattr);
156         uint32_t l1_index = L1_INDEX(va_);
157         uint32_t l2_index = L2_INDEX(va_);
158         if (!pp || !__vmm_map_internal(
159                      pid,
160                      l1_index, l2_index, (uintptr_t)pp, tattr, false)) {
161             // if one failed, release previous allocated pages.
162             va_ = va;
163             for (size_t j = 0; j < i; j++, va_ += PG_SIZE) {
164                 vmm_unmap_page(pid, va_);
165             }
166
167             return false;
168         }
169     }
170
171     return true;
172 }
173
174 int
175 vmm_set_mapping(pid_t pid, void* va, void* pa, pt_attr attr) {
176     assert(((uintptr_t)va & 0xFFFU) == 0);
177
178     uint32_t l1_index = L1_INDEX(va);
179     uint32_t l2_index = L2_INDEX(va);
180
181     // prevent map of recursive mapping region
182     if (l1_index == 1023) {
183         return 0;
184     }
185     
186     __vmm_map_internal(pid, l1_index, l2_index, (uintptr_t)pa, attr, false);
187     return 1;
188 }
189
190 void
191 __vmm_unmap_internal(pid_t pid, void* va, int free_ppage) {
192     assert(((uintptr_t)va & 0xFFFU) == 0);
193
194     uint32_t l1_index = L1_INDEX(va);
195     uint32_t l2_index = L2_INDEX(va);
196
197     // prevent unmap of recursive mapping region
198     if (l1_index == 1023) {
199         return;
200     }
201
202     x86_page_table* l1pt = (x86_page_table*)L1_BASE_VADDR;
203
204     x86_pte_t l1pte = l1pt->entry[l1_index];
205
206     if (l1pte) {
207         x86_page_table* l2pt = (x86_page_table*)L2_VADDR(l1_index);
208         x86_pte_t l2pte = l2pt->entry[l2_index];
209         if (IS_CACHED(l2pte) && free_ppage) {
210             pmm_free_page(pid, (void*)l2pte);
211         }
212         cpu_invplg(va);
213         l2pt->entry[l2_index] = PTE_NULL;
214     }
215 }
216
217 void
218 vmm_unset_mapping(void* va) {
219     __vmm_unmap_internal(0, va, false);
220 }
221
222 void
223 vmm_unmap_page(pid_t pid, void* va)
224 {
225     __vmm_unmap_internal(pid, va, true);
226 }
227
228 v_mapping
229 vmm_lookup(void* va)
230 {
231     assert(((uintptr_t)va & 0xFFFU) == 0);
232
233     uint32_t l1_index = L1_INDEX(va);
234     uint32_t l2_index = L2_INDEX(va);
235
236     x86_page_table* l1pt = (x86_page_table*)L1_BASE_VADDR;
237     x86_pte_t l1pte = l1pt->entry[l1_index];
238
239     v_mapping mapping = { .flags = 0, .pa = 0, .pn = 0 };
240     if (l1pte) {
241         x86_pte_t* l2pte =
242           &((x86_page_table*)L2_VADDR(l1_index))->entry[l2_index];
243         if (l2pte) {
244             mapping.flags = PG_ENTRY_FLAGS(*l2pte);
245             mapping.pa = PG_ENTRY_ADDR(*l2pte);
246             mapping.pn = mapping.pa >> PG_SIZE_BITS;
247             mapping.pte = l2pte;
248         }
249     }
250
251     return mapping;
252 }
253
254 void*
255 vmm_v2p(void* va)
256 {
257     return (void*)vmm_lookup(va).pa;
258 }