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