fixes and refinements
[lunaix-os.git] / lunaix-os / kernel / mm / vmm.c
1 #include <lunaix/mm/page.h>
2 #include <lunaix/mm/vmm.h>
3 #include <lunaix/mm/pmm.h>
4 #include <libc/string.h>
5
6 // TODO: Move these nasty inline asm stuff into hal
7 //      These should be arch dependent
8 ptd_t* get_pd() {
9     ptd_t* pd;
10     #ifdef __ARCH_IA32
11     __asm__(
12         "movl %%cr3, %0\n"
13         "andl $0xfffff000, %0"
14         : "=r"(pd)
15     );
16     #endif
17     return P2V(pd);
18 }
19
20 void set_pd(ptd_t* pd) {
21     #ifdef __ARCH_IA32
22     __asm__(
23         "movl %0, %%eax\n"
24         "andl $0xfffff000, %%eax\n"
25         "movl %%eax, %%cr3\n"
26         :
27         : "r" (pd)
28     );
29     #endif
30 }
31
32 void vmm_init() {
33     // TODO: something here?
34 }
35
36 ptd_t* vmm_init_pd() {
37     ptd_t* dir = pmm_alloc_page();
38     for (size_t i = 0; i < 1024; i++)
39     {
40         dir[i] = 0;
41     }
42     
43     // 自己映射自己,方便我们在软件层面进行查表地址转换
44     dir[1023] = PDE(T_SELF_REF_PERM, dir);
45
46     return dir;
47 }
48
49 void* vmm_map_page(void* va, void* pa, pt_attr dattr, pt_attr tattr) {
50     // 显然,对空指针进行映射没有意义。
51     if (!pa || !va) {
52         return NULL;
53     }
54
55     uintptr_t pd_offset = PD_INDEX(va);
56     uintptr_t pt_offset = PT_INDEX(va);
57     ptd_t* ptd = (ptd_t*)PTD_BASE_VADDR;
58
59     // 在页表与页目录中找到一个可用的空位进行映射(位于va或其附近)
60     ptd_t* pde = ptd[pd_offset];
61     pt_t* pt = (uintptr_t)PT_VADDR(pd_offset);
62     while (pde && pd_offset < 1024) {
63         if (pt_offset == 1024) {
64             pd_offset++;
65             pt_offset = 0;
66             pde = ptd[pd_offset];
67             pt = (pt_t*)PT_VADDR(pd_offset);
68         }
69         // 页表有空位,只需要开辟一个新的 PTE
70         if (pt && !pt[pt_offset]) {
71             pt[pt_offset] = PTE(tattr, pa);
72             return V_ADDR(pd_offset, pt_offset, PG_OFFSET(va));
73         }
74         pt_offset++;
75     }
76     
77     // 页目录与所有页表已满!
78     if (pd_offset > 1024) {
79         return NULL;
80     }
81
82     // 页目录有空位,需要开辟一个新的 PDE
83     uint8_t* new_pt_pa = pmm_alloc_page();
84     
85     // 物理内存已满!
86     if (!new_pt_pa) {
87         return NULL;
88     }
89     
90     ptd[pd_offset] = PDE(dattr, new_pt_pa);
91     
92     memset((void*)PT_VADDR(pd_offset), 0, PM_PAGE_SIZE);
93     pt[pt_offset] = PTE(tattr, pa);
94
95     return V_ADDR(pd_offset, pt_offset, PG_OFFSET(va));
96 }
97
98 void* vmm_alloc_page(void* vpn, pt_attr dattr, pt_attr tattr) {
99     void* pp = pmm_alloc_page();
100     void* result = vmm_map_page(vpn, pp, dattr, tattr);
101     if (!result) {
102         pmm_free_page(pp);
103     }
104     return result;
105 }
106
107 void vmm_unmap_page(void* vpn) {
108     uintptr_t pd_offset = PD_INDEX(vpn);
109     uintptr_t pt_offset = PT_INDEX(vpn);
110     ptd_t* self_pde = PTD_BASE_VADDR;
111
112     ptd_t pde = self_pde[pd_offset];
113
114     if (pde) {
115         pt_t* pt = (pt_t*)PT_VADDR(pd_offset);
116         uint32_t pte = pt[pt_offset];
117         if (IS_CACHED(pte) && pmm_free_page(pte)) {
118             // 刷新TLB
119             #ifdef __ARCH_IA32
120             __asm__("invlpg (%0)" :: "r"((uintptr_t)vpn) : "memory");
121             #endif
122         }
123         pt[pt_offset] = 0;
124     }
125 }
126
127 void* vmm_v2p(void* va) {
128     uintptr_t pd_offset = PD_INDEX(va);
129     uintptr_t pt_offset = PT_INDEX(va);
130     uintptr_t po = PG_OFFSET(va);
131     ptd_t* self_pde = PTD_BASE_VADDR;
132
133     ptd_t pde = self_pde[pd_offset];
134     if (pde) {
135         pt_t pte = ((pt_t*)PT_VADDR(pd_offset))[pt_offset];
136         if (pte) {
137             uintptr_t ppn = pte >> 12;
138             return (void*)P_ADDR(ppn, po);
139         }
140     }
141
142     return NULL;
143 }