93f33bd32ac9af35ee4e9378a6504467b741cc1c
[viridis.git] / mm / map.c
1 /* vim: set ts=4 sw=4 sts=4 et : */
2
3 #include <kernel.h>
4
5 #include <asm/regs.h>
6
7 static void __early_map_check(u64 * entry, u64 * target, u64 * alloc_base)
8 {
9     if (!(*entry & PF_P)) {
10         *entry = (*alloc_base) | (PF_RW | PF_P);
11
12         /* reset_cr3 so the newly mapped page is accessible, zero it, then
13          * reset cr3 again to make sure no crap mappings are in there. */
14
15         reset_cr3();
16         __clear_page(target);
17         reset_cr3();
18
19         *alloc_base = (*alloc_base) + PAGE_SIZE;
20     }
21 }
22
23 void *map_page_early(u64 virtual, u64 physical, u64 * alloc_base)
24 {
25     u64 *pml4e = (u64 *) PML4E_ADDR(virtual);
26     u64 *pdpe = (u64 *) PDPE_ADDR(virtual);
27     u64 *pde = (u64 *) PDE_ADDR(virtual);
28     u64 *pte = (u64 *) PTE_ADDR(virtual);
29
30     /* Make sure all of these structures exist, and if not, alloc and zero them */
31
32     __early_map_check(pml4e, pdpe, alloc_base);
33     __early_map_check(pdpe, pde, alloc_base);
34     __early_map_check(pde, pte, alloc_base);
35
36     *pte = PAGE_ALIGN(physical) | PF_RW | PF_P;
37
38     reset_cr3();
39
40     return (void *)virtual;
41 }
42
43 /* Similar to __early_map_check, except it can use page_alloc_phys, and check
44  * its return value*/
45
46 static int __map_check(u64 * entry, u64 * target)
47 {
48     u64 phys = 0;
49
50     if (!(*entry & PF_P)) {
51         /* Loop so we can leak pages that __clear_page determines are unresponsive */
52
53         /* This seems like it should be the physical page allocator's problem,
54          * but in order for it to check we'd have to make it depend on
55          * early_map with a known address. Considering this is literally the
56          * only consumer of page_alloc_phys outside of the page allocator and
57          * should remain so, instead perform the check here where we already
58          * have a known virtual target address.
59          */
60
61         do {
62             phys = page_alloc_phys(0);
63             if (phys == 0)
64                 return -ENOMEM;
65
66             *entry = (phys | PF_RW | PF_P);
67
68             reset_cr3();
69         } while(__clear_page(target));
70
71         reset_cr3();
72
73         return 1;
74     }
75
76     return 0;
77 }
78
79 int __map_page_flags(u64 virtual, u64 physical, u8 flags)
80 {
81     u64 *pml4e, *pdpe, *pde, *pte = NULL;
82     int ret, allocd = 0;
83
84     pml4e = (u64 *) PML4E_ADDR(virtual);
85     pdpe = (u64 *) PDPE_ADDR(virtual);
86     pde = (u64 *) PDE_ADDR(virtual);
87     pte = (u64 *) PTE_ADDR(virtual);
88
89     /* Make sure all of these structures exist, and if not, alloc and zero them */
90
91     ret = __map_check(pml4e, pdpe);
92     if (ret < 0)
93         return -ENOMEM;
94
95     allocd += ret;
96
97     ret = __map_check(pdpe, pde);
98     if (ret < 0)
99         goto cleanup_pml4e;
100
101     allocd += ret;
102
103     ret = __map_check(pde, pte);
104     if (ret < 0)
105         goto cleanup_pdpe;
106
107     *pte = PAGE_ALIGN(physical) | PF_P | flags;
108
109     return 0;
110
111  cleanup_pdpe:
112     if (allocd) {
113         page_alloc_free_phys(PAGE_ALIGN(*pdpe));
114         *pdpe = 0;
115         allocd--;
116     }
117  cleanup_pml4e:
118     if (allocd) {
119         page_alloc_free_phys(PAGE_ALIGN(*pml4e));
120         *pml4e = 0;
121     }
122
123     reset_cr3();
124
125     return -ENOMEM;
126 }
127
128 int __table_empty(u64 * table)
129 {
130     int i;
131     for (i = 0; i < 512; i++) {
132         if (table[i] & PF_P)
133             return 0;
134     }
135
136     return 1;
137 }
138
139 void __unmap_page(u64 virtual)
140 {
141     u64 *pml4e, *pdpe, *pde, *pte = NULL;
142     u64 *pdp, *pd, *pt;
143
144     pml4e = (u64 *) PML4E_ADDR(virtual);
145     pdpe = (u64 *) PDPE_ADDR(virtual);
146     pde = (u64 *) PDE_ADDR(virtual);
147     pte = (u64 *) PTE_ADDR(virtual);
148
149     pdp = (u64 *) PAGE_ALIGN((u64) pdpe);
150     pd = (u64 *) PAGE_ALIGN((u64) pde);
151     pt = (u64 *) PAGE_ALIGN((u64) pte);
152
153     /* If the mapping doesn't exist, get out */
154     if (!(*pml4e & PF_P))
155         return;
156     if (!(*pdpe & PF_P))
157         return;
158     if (!(*pde & PF_P))
159         return;
160     if (!(*pte & PF_P))
161         return;
162
163     *pte = 0;
164     if (__table_empty(pt)) {
165         page_alloc_free_phys(PAGE_ALIGN(*pde));
166         *pde = 0;
167
168         if (__table_empty(pd)) {
169             page_alloc_free_phys(PAGE_ALIGN(*pdpe));
170             *pdpe = 0;
171
172             if (__table_empty(pdp)) {
173                 page_alloc_free_phys(PAGE_ALIGN(*pml4e));
174                 *pml4e = 0;
175             }
176         }
177     }
178 }
179
180 void unmap_pages(u64 virtual, u64 num_pages)
181 {
182     while (num_pages) {
183         __unmap_page(virtual);
184         virtual += PAGE_SIZE;
185         num_pages--;
186     }
187 }
188
189 int __map_pages_flags(u64 virtual, u64 physical, u64 num_pages, u8 flags)
190 {
191     int i, j, ret;
192
193     for (i = 0; i < num_pages; i++) {
194         ret = __map_page_flags(virtual, physical, flags);
195
196         if (ret) {
197             for (j = i; j > 0; j--) {
198                 virtual -= PAGE_SIZE;
199                 physical -= PAGE_SIZE;
200                 unmap_pages(virtual, 1);
201             }
202             return ret;
203         }
204
205         virtual += PAGE_SIZE;
206         physical += PAGE_SIZE;
207     }
208
209     return 0;
210 }
211
212 int map_pages(u64 virtual, u64 physical, u64 num_pages)
213 {
214     int ret = __map_pages_flags(virtual, physical, num_pages, PF_RW);
215     reset_cr3();
216     return ret;
217 }
218
219 int map_pages_nocache(u64 virtual, u64 physical, u64 num_pages)
220 {
221     int ret =
222         __map_pages_flags(virtual, physical, num_pages,
223                           PF_RW | PF_DISABLE_CACHE);
224     reset_cr3();
225     return ret;
226 }
227
228 u64 map_virt_to_phys(u64 virtual)
229 {
230     u64 *pml4e = (u64 *) PML4E_ADDR(virtual);
231     u64 *pdpe = (u64 *) PDPE_ADDR(virtual);
232     u64 *pde = (u64 *) PDE_ADDR(virtual);
233     u64 *pte = (u64 *) PTE_ADDR(virtual);
234
235     if (!(*pml4e & PF_P))
236         return 0;
237     if (!(*pdpe & PF_P))
238         return 0;
239     if (!(*pde & PF_P))
240         return 0;
241     if (!(*pte & PF_P))
242         return 0;
243     return PAGE_ALIGN(*pte);
244 }
245
246 void map_page_summarize(u64 virtual)
247 {
248     u64 phys = map_virt_to_phys(virtual);
249
250     printk("M: 0x%lx -> 0x%lx\n", virtual, phys);
251 }