refactor: vmm_set_map has option to ignore existed mapping.
[lunaix-os.git] / lunaix-os / kernel / mm / vmm.c
1 #include <hal/cpu.h>
2 #include <klibc/string.h>
3 #include <lunaix/mm/pmm.h>
4 #include <lunaix/mm/vmm.h>
5 #include <lunaix/spike.h>
6
7 void
8 vmm_init()
9 {
10     // XXX: something here?
11 }
12
13 x86_page_table*
14 vmm_init_pd()
15 {
16     x86_page_table* dir =
17       (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_set_mapping(uintptr_t mnt,
30                 uintptr_t va,
31                 uintptr_t pa,
32                 pt_attr attr,
33                 int options)
34 {
35     assert((uintptr_t)va % PG_SIZE == 0);
36
37     uintptr_t l1_inx = L1_INDEX(va);
38     uintptr_t l2_inx = L2_INDEX(va);
39     x86_page_table* l1pt = (x86_page_table*)(mnt | (1023 << 12));
40     x86_page_table* l2pt = (x86_page_table*)(mnt | (l1_inx << 12));
41
42     // See if attr make sense
43     assert(attr <= 128);
44
45     if (!l1pt->entry[l1_inx]) {
46         x86_page_table* new_l1pt_pa = pmm_alloc_page(KERNEL_PID, PP_FGPERSIST);
47
48         // 物理内存已满!
49         if (!new_l1pt_pa) {
50             return 0;
51         }
52
53         // This must be writable
54         l1pt->entry[l1_inx] = NEW_L1_ENTRY(attr | PG_WRITE, new_l1pt_pa);
55         memset((void*)l2pt, 0, PG_SIZE);
56     } else {
57         x86_pte_t pte = l2pt->entry[l2_inx];
58         if (pte && (options & VMAP_IGNORE)) {
59             return 1;
60         }
61     }
62
63     if (mnt == PD_REFERENCED) {
64         cpu_invplg(va);
65     }
66
67     l2pt->entry[l2_inx] = NEW_L2_ENTRY(attr, pa);
68     return 1;
69 }
70
71 uintptr_t
72 vmm_del_mapping(uintptr_t mnt, uintptr_t va)
73 {
74     assert(((uintptr_t)va & 0xFFFU) == 0);
75
76     uint32_t l1_index = L1_INDEX(va);
77     uint32_t l2_index = L2_INDEX(va);
78
79     // prevent unmap of recursive mapping region
80     if (l1_index == 1023) {
81         return 0;
82     }
83
84     x86_page_table* l1pt = (x86_page_table*)(mnt | (1023 << 12));
85
86     x86_pte_t l1pte = l1pt->entry[l1_index];
87
88     if (l1pte) {
89         x86_page_table* l2pt = (x86_page_table*)(mnt | (l1_index << 12));
90         x86_pte_t l2pte = l2pt->entry[l2_index];
91
92         cpu_invplg(va);
93         l2pt->entry[l2_index] = PTE_NULL;
94
95         return PG_ENTRY_ADDR(l2pte);
96     }
97
98     return 0;
99 }
100
101 int
102 vmm_lookup(uintptr_t va, v_mapping* mapping)
103 {
104     uint32_t l1_index = L1_INDEX(va);
105     uint32_t l2_index = L2_INDEX(va);
106
107     x86_page_table* l1pt = (x86_page_table*)L1_BASE_VADDR;
108     x86_pte_t l1pte = l1pt->entry[l1_index];
109
110     if (l1pte) {
111         x86_pte_t* l2pte =
112           &((x86_page_table*)L2_VADDR(l1_index))->entry[l2_index];
113         if (l2pte) {
114             mapping->flags = PG_ENTRY_FLAGS(*l2pte);
115             mapping->pa = PG_ENTRY_ADDR(*l2pte);
116             mapping->pn = mapping->pa >> PG_SIZE_BITS;
117             mapping->pte = l2pte;
118             mapping->va = va;
119             return 1;
120         }
121     }
122     return 0;
123 }
124
125 void*
126 vmm_mount_pd(uintptr_t mnt, void* pde)
127 {
128     x86_page_table* l1pt = (x86_page_table*)L1_BASE_VADDR;
129     l1pt->entry[(mnt >> 22)] = NEW_L1_ENTRY(T_SELF_REF_PERM, pde);
130     cpu_invplg(mnt);
131     return mnt;
132 }
133
134 void*
135 vmm_unmount_pd(uintptr_t mnt)
136 {
137     x86_page_table* l1pt = (x86_page_table*)L1_BASE_VADDR;
138     l1pt->entry[(mnt >> 22)] = 0;
139     cpu_invplg(mnt);
140 }