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