rev |
line source |
nuclear@17
|
1 #include <stdio.h>
|
nuclear@17
|
2 #include <string.h>
|
nuclear@17
|
3 #include <inttypes.h>
|
nuclear@52
|
4 #include <assert.h>
|
nuclear@52
|
5 #include "config.h"
|
nuclear@17
|
6 #include "vm.h"
|
nuclear@17
|
7 #include "intr.h"
|
nuclear@17
|
8 #include "mem.h"
|
nuclear@17
|
9 #include "panic.h"
|
nuclear@52
|
10 #include "proc.h"
|
nuclear@17
|
11
|
nuclear@17
|
12 #define IDMAP_START 0xa0000
|
nuclear@17
|
13
|
nuclear@24
|
14 #define PGDIR_ADDR 0xfffff000
|
nuclear@24
|
15 #define PGTBL_BASE (0xffffffff - 4096 * 1024 + 1)
|
nuclear@24
|
16 #define PGTBL(x) ((uint32_t*)(PGTBL_BASE + PGSIZE * (x)))
|
nuclear@24
|
17
|
nuclear@17
|
18 #define ATTR_PGDIR_MASK 0x3f
|
nuclear@17
|
19 #define ATTR_PGTBL_MASK 0x1ff
|
nuclear@17
|
20 #define ADDR_PGENT_MASK 0xfffff000
|
nuclear@17
|
21
|
nuclear@17
|
22 #define PAGEFAULT 14
|
nuclear@17
|
23
|
nuclear@22
|
24
|
nuclear@22
|
25 struct page_range {
|
nuclear@22
|
26 int start, end;
|
nuclear@22
|
27 struct page_range *next;
|
nuclear@22
|
28 };
|
nuclear@22
|
29
|
nuclear@22
|
30 /* defined in vm-asm.S */
|
nuclear@22
|
31 void enable_paging(void);
|
nuclear@23
|
32 void disable_paging(void);
|
nuclear@23
|
33 int get_paging_status(void);
|
nuclear@22
|
34 void set_pgdir_addr(uint32_t addr);
|
nuclear@23
|
35 void flush_tlb(void);
|
nuclear@23
|
36 void flush_tlb_addr(uint32_t addr);
|
nuclear@23
|
37 #define flush_tlb_page(p) flush_tlb_addr(PAGE_TO_ADDR(p))
|
nuclear@22
|
38 uint32_t get_fault_addr(void);
|
nuclear@22
|
39
|
nuclear@23
|
40 static void coalesce(struct page_range *low, struct page_range *mid, struct page_range *high);
|
nuclear@52
|
41 static void pgfault(int inum);
|
nuclear@22
|
42 static struct page_range *alloc_node(void);
|
nuclear@22
|
43 static void free_node(struct page_range *node);
|
nuclear@22
|
44
|
nuclear@22
|
45 /* page directory */
|
nuclear@22
|
46 static uint32_t *pgdir;
|
nuclear@22
|
47
|
nuclear@22
|
48 /* 2 lists of free ranges, for kernel memory and user memory */
|
nuclear@22
|
49 static struct page_range *pglist[2];
|
nuclear@22
|
50 /* list of free page_range structures to be used in the lists */
|
nuclear@22
|
51 static struct page_range *node_pool;
|
nuclear@23
|
52 /* the first page range for the whole kernel address space, to get things started */
|
nuclear@23
|
53 static struct page_range first_node;
|
nuclear@22
|
54
|
nuclear@22
|
55
|
nuclear@26
|
56 void init_vm(void)
|
nuclear@17
|
57 {
|
nuclear@19
|
58 uint32_t idmap_end;
|
nuclear@47
|
59 int i, kmem_start_pg, pgtbl_base_pg;
|
nuclear@19
|
60
|
nuclear@23
|
61 /* setup the page tables */
|
nuclear@18
|
62 pgdir = (uint32_t*)alloc_phys_page();
|
nuclear@23
|
63 memset(pgdir, 0, PGSIZE);
|
nuclear@24
|
64 set_pgdir_addr((uint32_t)pgdir);
|
nuclear@17
|
65
|
nuclear@17
|
66 /* map the video memory and kernel code 1-1 */
|
nuclear@19
|
67 get_kernel_mem_range(0, &idmap_end);
|
nuclear@19
|
68 map_mem_range(IDMAP_START, idmap_end - IDMAP_START, IDMAP_START, 0);
|
nuclear@17
|
69
|
nuclear@24
|
70 /* make the last page directory entry point to the page directory */
|
nuclear@24
|
71 pgdir[1023] = ((uint32_t)pgdir & ADDR_PGENT_MASK) | PG_PRESENT;
|
nuclear@24
|
72 pgdir = (uint32_t*)PGDIR_ADDR;
|
nuclear@24
|
73
|
nuclear@23
|
74 /* set the page fault handler */
|
nuclear@17
|
75 interrupt(PAGEFAULT, pgfault);
|
nuclear@17
|
76
|
nuclear@23
|
77 /* we can enable paging now */
|
nuclear@17
|
78 enable_paging();
|
nuclear@23
|
79
|
nuclear@23
|
80 /* initialize the virtual page allocator */
|
nuclear@23
|
81 node_pool = 0;
|
nuclear@23
|
82
|
nuclear@47
|
83 kmem_start_pg = ADDR_TO_PAGE(KMEM_START);
|
nuclear@47
|
84 pgtbl_base_pg = ADDR_TO_PAGE(PGTBL_BASE);
|
nuclear@47
|
85
|
nuclear@47
|
86 first_node.start = kmem_start_pg;
|
nuclear@47
|
87 first_node.end = pgtbl_base_pg;
|
nuclear@23
|
88 first_node.next = 0;
|
nuclear@23
|
89 pglist[MEM_KERNEL] = &first_node;
|
nuclear@23
|
90
|
nuclear@23
|
91 pglist[MEM_USER] = alloc_node();
|
nuclear@26
|
92 pglist[MEM_USER]->start = ADDR_TO_PAGE(idmap_end);
|
nuclear@47
|
93 pglist[MEM_USER]->end = kmem_start_pg;
|
nuclear@23
|
94 pglist[MEM_USER]->next = 0;
|
nuclear@47
|
95
|
nuclear@47
|
96 /* temporaroly map something into every 1024th page of the kernel address
|
nuclear@47
|
97 * space to force pre-allocation of all the kernel page-tables
|
nuclear@47
|
98 */
|
nuclear@47
|
99 for(i=kmem_start_pg; i<pgtbl_base_pg; i+=1024) {
|
nuclear@47
|
100 /* if there's already something mapped here, leave it alone */
|
nuclear@47
|
101 if(virt_to_phys_page(i) == -1) {
|
nuclear@47
|
102 map_page(i, 0, 0);
|
nuclear@47
|
103 unmap_page(i);
|
nuclear@47
|
104 }
|
nuclear@47
|
105 }
|
nuclear@17
|
106 }
|
nuclear@17
|
107
|
nuclear@23
|
108 /* if ppage == -1 we allocate a physical page by calling alloc_phys_page */
|
nuclear@23
|
109 int map_page(int vpage, int ppage, unsigned int attr)
|
nuclear@17
|
110 {
|
nuclear@17
|
111 uint32_t *pgtbl;
|
nuclear@25
|
112 int diridx, pgidx, pgon, intr_state;
|
nuclear@25
|
113
|
nuclear@25
|
114 intr_state = get_intr_state();
|
nuclear@25
|
115 disable_intr();
|
nuclear@23
|
116
|
nuclear@23
|
117 pgon = get_paging_status();
|
nuclear@23
|
118
|
nuclear@23
|
119 if(ppage < 0) {
|
nuclear@23
|
120 uint32_t addr = alloc_phys_page();
|
nuclear@23
|
121 if(!addr) {
|
nuclear@25
|
122 set_intr_state(intr_state);
|
nuclear@23
|
123 return -1;
|
nuclear@23
|
124 }
|
nuclear@23
|
125 ppage = ADDR_TO_PAGE(addr);
|
nuclear@23
|
126 }
|
nuclear@23
|
127
|
nuclear@23
|
128 diridx = PAGE_TO_PGTBL(vpage);
|
nuclear@23
|
129 pgidx = PAGE_TO_PGTBL_PG(vpage);
|
nuclear@17
|
130
|
nuclear@17
|
131 if(!(pgdir[diridx] & PG_PRESENT)) {
|
nuclear@55
|
132 /* no page table present, we must allocate one */
|
nuclear@17
|
133 uint32_t addr = alloc_phys_page();
|
nuclear@55
|
134
|
nuclear@55
|
135 /* make sure all page directory entries in the below the kernel vm
|
nuclear@55
|
136 * split have the user and writable bits set, otherwise further user
|
nuclear@55
|
137 * mappings on the same 4mb block will be unusable in user space.
|
nuclear@55
|
138 */
|
nuclear@55
|
139 unsigned int pgdir_attr = attr;
|
nuclear@55
|
140 if(vpage < ADDR_TO_PAGE(KMEM_START)) {
|
nuclear@55
|
141 pgdir_attr |= PG_USER | PG_WRITABLE;
|
nuclear@55
|
142 }
|
nuclear@55
|
143
|
nuclear@55
|
144 pgdir[diridx] = addr | (pgdir_attr & ATTR_PGDIR_MASK) | PG_PRESENT;
|
nuclear@24
|
145
|
nuclear@24
|
146 pgtbl = pgon ? PGTBL(diridx) : (uint32_t*)addr;
|
nuclear@18
|
147 memset(pgtbl, 0, PGSIZE);
|
nuclear@17
|
148 } else {
|
nuclear@24
|
149 if(pgon) {
|
nuclear@24
|
150 pgtbl = PGTBL(diridx);
|
nuclear@24
|
151 } else {
|
nuclear@24
|
152 pgtbl = (uint32_t*)(pgdir[diridx] & ADDR_PGENT_MASK);
|
nuclear@24
|
153 }
|
nuclear@17
|
154 }
|
nuclear@17
|
155
|
nuclear@17
|
156 pgtbl[pgidx] = PAGE_TO_ADDR(ppage) | (attr & ATTR_PGTBL_MASK) | PG_PRESENT;
|
nuclear@23
|
157 flush_tlb_page(vpage);
|
nuclear@23
|
158
|
nuclear@25
|
159 set_intr_state(intr_state);
|
nuclear@23
|
160 return 0;
|
nuclear@17
|
161 }
|
nuclear@17
|
162
|
nuclear@43
|
163 int unmap_page(int vpage)
|
nuclear@17
|
164 {
|
nuclear@17
|
165 uint32_t *pgtbl;
|
nuclear@43
|
166 int res = 0;
|
nuclear@17
|
167 int diridx = PAGE_TO_PGTBL(vpage);
|
nuclear@17
|
168 int pgidx = PAGE_TO_PGTBL_PG(vpage);
|
nuclear@17
|
169
|
nuclear@25
|
170 int intr_state = get_intr_state();
|
nuclear@25
|
171 disable_intr();
|
nuclear@25
|
172
|
nuclear@17
|
173 if(!(pgdir[diridx] & PG_PRESENT)) {
|
nuclear@17
|
174 goto err;
|
nuclear@17
|
175 }
|
nuclear@26
|
176 pgtbl = PGTBL(diridx);
|
nuclear@17
|
177
|
nuclear@17
|
178 if(!(pgtbl[pgidx] & PG_PRESENT)) {
|
nuclear@17
|
179 goto err;
|
nuclear@17
|
180 }
|
nuclear@17
|
181 pgtbl[pgidx] = 0;
|
nuclear@23
|
182 flush_tlb_page(vpage);
|
nuclear@17
|
183
|
nuclear@25
|
184 if(0) {
|
nuclear@17
|
185 err:
|
nuclear@25
|
186 printf("unmap_page(%d): page already not mapped\n", vpage);
|
nuclear@43
|
187 res = -1;
|
nuclear@25
|
188 }
|
nuclear@25
|
189 set_intr_state(intr_state);
|
nuclear@43
|
190 return res;
|
nuclear@17
|
191 }
|
nuclear@17
|
192
|
nuclear@22
|
193 /* if ppg_start is -1, we allocate physical pages to map with alloc_phys_page() */
|
nuclear@23
|
194 int map_page_range(int vpg_start, int pgcount, int ppg_start, unsigned int attr)
|
nuclear@17
|
195 {
|
nuclear@23
|
196 int i, phys_pg;
|
nuclear@17
|
197
|
nuclear@17
|
198 for(i=0; i<pgcount; i++) {
|
nuclear@26
|
199 phys_pg = ppg_start < 0 ? -1 : ppg_start + i;
|
nuclear@23
|
200 map_page(vpg_start + i, phys_pg, attr);
|
nuclear@17
|
201 }
|
nuclear@23
|
202 return 0;
|
nuclear@17
|
203 }
|
nuclear@17
|
204
|
nuclear@43
|
205 int unmap_page_range(int vpg_start, int pgcount)
|
nuclear@43
|
206 {
|
nuclear@43
|
207 int i, res = 0;
|
nuclear@43
|
208
|
nuclear@43
|
209 for(i=0; i<pgcount; i++) {
|
nuclear@43
|
210 if(unmap_page(vpg_start + i) == -1) {
|
nuclear@43
|
211 res = -1;
|
nuclear@43
|
212 }
|
nuclear@43
|
213 }
|
nuclear@43
|
214 return res;
|
nuclear@43
|
215 }
|
nuclear@43
|
216
|
nuclear@23
|
217 /* if paddr is 0, we allocate physical pages with alloc_phys_page() */
|
nuclear@23
|
218 int map_mem_range(uint32_t vaddr, size_t sz, uint32_t paddr, unsigned int attr)
|
nuclear@17
|
219 {
|
nuclear@17
|
220 int vpg_start, ppg_start, num_pages;
|
nuclear@17
|
221
|
nuclear@23
|
222 if(!sz) return -1;
|
nuclear@17
|
223
|
nuclear@17
|
224 if(ADDR_TO_PGOFFS(paddr)) {
|
nuclear@17
|
225 panic("map_mem_range called with unaligned physical address: %x\n", paddr);
|
nuclear@17
|
226 }
|
nuclear@17
|
227
|
nuclear@17
|
228 vpg_start = ADDR_TO_PAGE(vaddr);
|
nuclear@23
|
229 ppg_start = paddr > 0 ? ADDR_TO_PAGE(paddr) : -1;
|
nuclear@17
|
230 num_pages = ADDR_TO_PAGE(sz) + 1;
|
nuclear@17
|
231
|
nuclear@23
|
232 return map_page_range(vpg_start, num_pages, ppg_start, attr);
|
nuclear@17
|
233 }
|
nuclear@17
|
234
|
nuclear@18
|
235 uint32_t virt_to_phys(uint32_t vaddr)
|
nuclear@18
|
236 {
|
nuclear@43
|
237 int pg;
|
nuclear@43
|
238 uint32_t pgaddr;
|
nuclear@43
|
239
|
nuclear@43
|
240 if((pg = virt_to_phys_page(ADDR_TO_PAGE(vaddr))) == -1) {
|
nuclear@43
|
241 return 0;
|
nuclear@43
|
242 }
|
nuclear@43
|
243 pgaddr = PAGE_TO_ADDR(pg);
|
nuclear@43
|
244
|
nuclear@43
|
245 return pgaddr | ADDR_TO_PGOFFS(vaddr);
|
nuclear@43
|
246 }
|
nuclear@43
|
247
|
nuclear@43
|
248 int virt_to_phys_page(int vpg)
|
nuclear@43
|
249 {
|
nuclear@18
|
250 uint32_t pgaddr, *pgtbl;
|
nuclear@43
|
251 int diridx, pgidx;
|
nuclear@43
|
252
|
nuclear@43
|
253 if(vpg < 0 || vpg >= PAGE_COUNT) {
|
nuclear@43
|
254 return -1;
|
nuclear@43
|
255 }
|
nuclear@43
|
256
|
nuclear@43
|
257 diridx = PAGE_TO_PGTBL(vpg);
|
nuclear@43
|
258 pgidx = PAGE_TO_PGTBL_PG(vpg);
|
nuclear@18
|
259
|
nuclear@18
|
260 if(!(pgdir[diridx] & PG_PRESENT)) {
|
nuclear@43
|
261 return -1;
|
nuclear@18
|
262 }
|
nuclear@26
|
263 pgtbl = PGTBL(diridx);
|
nuclear@18
|
264
|
nuclear@18
|
265 if(!(pgtbl[pgidx] & PG_PRESENT)) {
|
nuclear@43
|
266 return -1;
|
nuclear@18
|
267 }
|
nuclear@18
|
268 pgaddr = pgtbl[pgidx] & PGENT_ADDR_MASK;
|
nuclear@43
|
269 return ADDR_TO_PAGE(pgaddr);
|
nuclear@18
|
270 }
|
nuclear@18
|
271
|
nuclear@22
|
272 /* allocate a contiguous block of virtual memory pages along with
|
nuclear@22
|
273 * backing physical memory for them, and update the page table.
|
nuclear@22
|
274 */
|
nuclear@22
|
275 int pgalloc(int num, int area)
|
nuclear@22
|
276 {
|
nuclear@25
|
277 int intr_state, ret = -1;
|
nuclear@22
|
278 struct page_range *node, *prev, dummy;
|
nuclear@22
|
279
|
nuclear@25
|
280 intr_state = get_intr_state();
|
nuclear@25
|
281 disable_intr();
|
nuclear@25
|
282
|
nuclear@22
|
283 dummy.next = pglist[area];
|
nuclear@22
|
284 node = pglist[area];
|
nuclear@22
|
285 prev = &dummy;
|
nuclear@22
|
286
|
nuclear@22
|
287 while(node) {
|
nuclear@22
|
288 if(node->end - node->start >= num) {
|
nuclear@22
|
289 ret = node->start;
|
nuclear@22
|
290 node->start += num;
|
nuclear@22
|
291
|
nuclear@22
|
292 if(node->start == node->end) {
|
nuclear@22
|
293 prev->next = node->next;
|
nuclear@22
|
294 node->next = 0;
|
nuclear@22
|
295
|
nuclear@22
|
296 if(node == pglist[area]) {
|
nuclear@22
|
297 pglist[area] = 0;
|
nuclear@22
|
298 }
|
nuclear@22
|
299 free_node(node);
|
nuclear@22
|
300 }
|
nuclear@22
|
301 break;
|
nuclear@22
|
302 }
|
nuclear@22
|
303
|
nuclear@22
|
304 prev = node;
|
nuclear@22
|
305 node = node->next;
|
nuclear@22
|
306 }
|
nuclear@22
|
307
|
nuclear@22
|
308 if(ret >= 0) {
|
nuclear@55
|
309 /*unsigned int attr = (area == MEM_USER) ? (PG_USER | PG_WRITABLE) : PG_GLOBAL;*/
|
nuclear@55
|
310 unsigned int attr = (area == MEM_USER) ? (PG_USER | PG_WRITABLE) : 0;
|
nuclear@55
|
311
|
nuclear@23
|
312 /* allocate physical storage and map */
|
nuclear@45
|
313 if(map_page_range(ret, num, -1, attr) == -1) {
|
nuclear@45
|
314 ret = -1;
|
nuclear@45
|
315 }
|
nuclear@45
|
316 }
|
nuclear@45
|
317
|
nuclear@45
|
318 set_intr_state(intr_state);
|
nuclear@45
|
319 return ret;
|
nuclear@45
|
320 }
|
nuclear@45
|
321
|
nuclear@45
|
322 int pgalloc_vrange(int start, int num)
|
nuclear@45
|
323 {
|
nuclear@45
|
324 struct page_range *node, *prev, dummy;
|
nuclear@45
|
325 int area, intr_state, ret = -1;
|
nuclear@45
|
326
|
nuclear@45
|
327 area = (start >= ADDR_TO_PAGE(KMEM_START)) ? MEM_KERNEL : MEM_USER;
|
nuclear@47
|
328 if(area == MEM_USER && start + num > ADDR_TO_PAGE(KMEM_START)) {
|
nuclear@45
|
329 printf("pgalloc_vrange: invalid range request crossing user/kernel split\n");
|
nuclear@45
|
330 return -1;
|
nuclear@45
|
331 }
|
nuclear@45
|
332
|
nuclear@45
|
333 intr_state = get_intr_state();
|
nuclear@45
|
334 disable_intr();
|
nuclear@45
|
335
|
nuclear@45
|
336 dummy.next = pglist[area];
|
nuclear@45
|
337 node = pglist[area];
|
nuclear@45
|
338 prev = &dummy;
|
nuclear@45
|
339
|
nuclear@45
|
340 /* check to see if the requested VM range is available */
|
nuclear@45
|
341 node = pglist[area];
|
nuclear@45
|
342 while(node) {
|
nuclear@45
|
343 if(start >= node->start && start + num <= node->end) {
|
nuclear@49
|
344 ret = start; /* can do .. */
|
nuclear@49
|
345
|
nuclear@49
|
346 if(start == node->start) {
|
nuclear@49
|
347 /* adjacent to the start of the range */
|
nuclear@49
|
348 node->start += num;
|
nuclear@49
|
349 } else if(start + num == node->end) {
|
nuclear@49
|
350 /* adjacent to the end of the range */
|
nuclear@49
|
351 node->end = start;
|
nuclear@49
|
352 } else {
|
nuclear@49
|
353 /* somewhere in the middle, which means we need
|
nuclear@49
|
354 * to allocate a new page_range
|
nuclear@49
|
355 */
|
nuclear@49
|
356 struct page_range *newnode;
|
nuclear@49
|
357
|
nuclear@49
|
358 if(!(newnode = alloc_node())) {
|
nuclear@49
|
359 panic("pgalloc_vrange failed to allocate new page_range while splitting a range in half... bummer\n");
|
nuclear@49
|
360 }
|
nuclear@49
|
361 newnode->start = start + num;
|
nuclear@49
|
362 newnode->end = node->end;
|
nuclear@49
|
363 newnode->next = node->next;
|
nuclear@49
|
364
|
nuclear@49
|
365 node->end = start;
|
nuclear@49
|
366 node->next = newnode;
|
nuclear@49
|
367 /* no need to check for null nodes at this point, there's
|
nuclear@49
|
368 * certainly stuff at the begining and the end, otherwise we
|
nuclear@49
|
369 * wouldn't be here. so break out of it.
|
nuclear@49
|
370 */
|
nuclear@49
|
371 break;
|
nuclear@49
|
372 }
|
nuclear@45
|
373
|
nuclear@45
|
374 if(node->start == node->end) {
|
nuclear@45
|
375 prev->next = node->next;
|
nuclear@45
|
376 node->next = 0;
|
nuclear@45
|
377
|
nuclear@45
|
378 if(node == pglist[area]) {
|
nuclear@45
|
379 pglist[area] = 0;
|
nuclear@45
|
380 }
|
nuclear@45
|
381 free_node(node);
|
nuclear@45
|
382 }
|
nuclear@45
|
383 break;
|
nuclear@45
|
384 }
|
nuclear@45
|
385
|
nuclear@45
|
386 prev = node;
|
nuclear@45
|
387 node = node->next;
|
nuclear@45
|
388 }
|
nuclear@45
|
389
|
nuclear@45
|
390 if(ret >= 0) {
|
nuclear@55
|
391 /*unsigned int attr = (area == MEM_USER) ? (PG_USER | PG_WRITABLE) : PG_GLOBAL;*/
|
nuclear@55
|
392 unsigned int attr = (area == MEM_USER) ? (PG_USER | PG_WRITABLE) : 0;
|
nuclear@55
|
393
|
nuclear@45
|
394 /* allocate physical storage and map */
|
nuclear@45
|
395 if(map_page_range(ret, num, -1, attr) == -1) {
|
nuclear@23
|
396 ret = -1;
|
nuclear@23
|
397 }
|
nuclear@22
|
398 }
|
nuclear@22
|
399
|
nuclear@25
|
400 set_intr_state(intr_state);
|
nuclear@22
|
401 return ret;
|
nuclear@22
|
402 }
|
nuclear@22
|
403
|
nuclear@22
|
404 void pgfree(int start, int num)
|
nuclear@22
|
405 {
|
nuclear@33
|
406 int i, area, intr_state;
|
nuclear@23
|
407 struct page_range *node, *new, *prev, *next;
|
nuclear@23
|
408
|
nuclear@25
|
409 intr_state = get_intr_state();
|
nuclear@25
|
410 disable_intr();
|
nuclear@25
|
411
|
nuclear@26
|
412 for(i=0; i<num; i++) {
|
nuclear@43
|
413 int phys_pg = virt_to_phys_page(start + i);
|
nuclear@43
|
414 if(phys_pg != -1) {
|
nuclear@43
|
415 free_phys_page(phys_pg);
|
nuclear@26
|
416 }
|
nuclear@26
|
417 }
|
nuclear@26
|
418
|
nuclear@23
|
419 if(!(new = alloc_node())) {
|
nuclear@23
|
420 panic("pgfree: can't allocate new page_range node to add the freed pages\n");
|
nuclear@23
|
421 }
|
nuclear@23
|
422 new->start = start;
|
nuclear@33
|
423 new->end = start + num;
|
nuclear@23
|
424
|
nuclear@23
|
425 area = PAGE_TO_ADDR(start) >= KMEM_START ? MEM_KERNEL : MEM_USER;
|
nuclear@23
|
426
|
nuclear@23
|
427 if(!pglist[area] || pglist[area]->start > start) {
|
nuclear@23
|
428 next = new->next = pglist[area];
|
nuclear@23
|
429 pglist[area] = new;
|
nuclear@23
|
430 prev = 0;
|
nuclear@23
|
431
|
nuclear@23
|
432 } else {
|
nuclear@23
|
433
|
nuclear@23
|
434 prev = 0;
|
nuclear@23
|
435 node = pglist[area];
|
nuclear@23
|
436 next = node ? node->next : 0;
|
nuclear@23
|
437
|
nuclear@23
|
438 while(node) {
|
nuclear@23
|
439 if(!next || next->start > start) {
|
nuclear@23
|
440 /* place here, after node */
|
nuclear@23
|
441 new->next = next;
|
nuclear@23
|
442 node->next = new;
|
nuclear@23
|
443 prev = node; /* needed by coalesce after the loop */
|
nuclear@23
|
444 break;
|
nuclear@23
|
445 }
|
nuclear@23
|
446
|
nuclear@23
|
447 prev = node;
|
nuclear@23
|
448 node = next;
|
nuclear@23
|
449 next = node ? node->next : 0;
|
nuclear@23
|
450 }
|
nuclear@23
|
451 }
|
nuclear@23
|
452
|
nuclear@23
|
453 coalesce(prev, new, next);
|
nuclear@25
|
454 set_intr_state(intr_state);
|
nuclear@23
|
455 }
|
nuclear@23
|
456
|
nuclear@23
|
457 static void coalesce(struct page_range *low, struct page_range *mid, struct page_range *high)
|
nuclear@23
|
458 {
|
nuclear@23
|
459 if(high) {
|
nuclear@23
|
460 if(mid->end == high->start) {
|
nuclear@23
|
461 mid->end = high->end;
|
nuclear@23
|
462 mid->next = high->next;
|
nuclear@23
|
463 free_node(high);
|
nuclear@23
|
464 }
|
nuclear@23
|
465 }
|
nuclear@23
|
466
|
nuclear@23
|
467 if(low) {
|
nuclear@23
|
468 if(low->end == mid->start) {
|
nuclear@23
|
469 low->end += mid->end;
|
nuclear@23
|
470 low->next = mid->next;
|
nuclear@23
|
471 free_node(mid);
|
nuclear@23
|
472 }
|
nuclear@23
|
473 }
|
nuclear@22
|
474 }
|
nuclear@22
|
475
|
nuclear@52
|
476 static void pgfault(int inum)
|
nuclear@17
|
477 {
|
nuclear@52
|
478 struct intr_frame *frm = get_intr_frame();
|
nuclear@52
|
479 uint32_t fault_addr = get_fault_addr();
|
nuclear@52
|
480
|
nuclear@52
|
481 /* the fault occured in user space */
|
nuclear@55
|
482 if(frm->err & PG_USER) {
|
nuclear@52
|
483 int fault_page = ADDR_TO_PAGE(fault_addr);
|
nuclear@52
|
484 struct process *proc = get_current_proc();
|
nuclear@55
|
485 printf("DBG: page fault in user space\n");
|
nuclear@52
|
486 assert(proc);
|
nuclear@52
|
487
|
nuclear@52
|
488 if(frm->err & PG_PRESENT) {
|
nuclear@52
|
489 /* it's not due to a missing page, just panic */
|
nuclear@52
|
490 goto unhandled;
|
nuclear@52
|
491 }
|
nuclear@52
|
492
|
nuclear@52
|
493 /* detect if it's an automatic stack growth deal */
|
nuclear@55
|
494 if(fault_page < proc->user_stack_pg && proc->user_stack_pg - fault_page < USTACK_MAXGROW) {
|
nuclear@55
|
495 int num_pages = proc->user_stack_pg - fault_page;
|
nuclear@52
|
496 printf("growing user (%d) stack by %d pages\n", proc->id, num_pages);
|
nuclear@52
|
497
|
nuclear@52
|
498 if(pgalloc_vrange(fault_page, num_pages) != fault_page) {
|
nuclear@52
|
499 printf("failed to allocate VM for stack growth\n");
|
nuclear@52
|
500 /* TODO: in the future we'd SIGSEGV the process here, for now just panic */
|
nuclear@52
|
501 goto unhandled;
|
nuclear@52
|
502 }
|
nuclear@55
|
503 proc->user_stack_pg = fault_page;
|
nuclear@52
|
504 return;
|
nuclear@52
|
505 }
|
nuclear@52
|
506 }
|
nuclear@52
|
507
|
nuclear@52
|
508 unhandled:
|
nuclear@17
|
509 printf("~~~~ PAGE FAULT ~~~~\n");
|
nuclear@52
|
510 printf("fault address: %x\n", fault_addr);
|
nuclear@17
|
511
|
nuclear@51
|
512 if(frm->err & PG_PRESENT) {
|
nuclear@51
|
513 if(frm->err & 8) {
|
nuclear@17
|
514 printf("reserved bit set in some paging structure\n");
|
nuclear@17
|
515 } else {
|
nuclear@55
|
516 printf("%s protection violation ", (frm->err & PG_WRITABLE) ? "WRITE" : "READ");
|
nuclear@55
|
517 printf("in %s mode\n", (frm->err & PG_USER) ? "user" : "kernel");
|
nuclear@17
|
518 }
|
nuclear@17
|
519 } else {
|
nuclear@17
|
520 printf("page not present\n");
|
nuclear@17
|
521 }
|
nuclear@19
|
522
|
nuclear@19
|
523 panic("unhandled page fault\n");
|
nuclear@17
|
524 }
|
nuclear@22
|
525
|
nuclear@22
|
526 /* --- page range list node management --- */
|
nuclear@23
|
527 #define NODES_IN_PAGE (PGSIZE / sizeof(struct page_range))
|
nuclear@23
|
528
|
nuclear@22
|
529 static struct page_range *alloc_node(void)
|
nuclear@22
|
530 {
|
nuclear@22
|
531 struct page_range *node;
|
nuclear@23
|
532 int pg, i;
|
nuclear@22
|
533
|
nuclear@22
|
534 if(node_pool) {
|
nuclear@22
|
535 node = node_pool;
|
nuclear@22
|
536 node_pool = node_pool->next;
|
nuclear@47
|
537 /*printf("alloc_node -> %x\n", (unsigned int)node);*/
|
nuclear@22
|
538 return node;
|
nuclear@22
|
539 }
|
nuclear@22
|
540
|
nuclear@23
|
541 /* no node structures in the pool, we need to allocate a new page,
|
nuclear@23
|
542 * split it up into node structures, add them in the pool, and
|
nuclear@23
|
543 * allocate one of them.
|
nuclear@22
|
544 */
|
nuclear@23
|
545 if(!(pg = pgalloc(1, MEM_KERNEL))) {
|
nuclear@22
|
546 panic("ran out of physical memory while allocating VM range structures\n");
|
nuclear@22
|
547 }
|
nuclear@23
|
548 node_pool = (struct page_range*)PAGE_TO_ADDR(pg);
|
nuclear@22
|
549
|
nuclear@23
|
550 /* link them up, skip the first as we'll just allocate it anyway */
|
nuclear@23
|
551 for(i=2; i<NODES_IN_PAGE; i++) {
|
nuclear@23
|
552 node_pool[i - 1].next = node_pool + i;
|
nuclear@23
|
553 }
|
nuclear@23
|
554 node_pool[NODES_IN_PAGE - 1].next = 0;
|
nuclear@23
|
555
|
nuclear@23
|
556 /* grab the first and return it */
|
nuclear@23
|
557 node = node_pool++;
|
nuclear@47
|
558 /*printf("alloc_node -> %x\n", (unsigned int)node);*/
|
nuclear@23
|
559 return node;
|
nuclear@22
|
560 }
|
nuclear@22
|
561
|
nuclear@22
|
562 static void free_node(struct page_range *node)
|
nuclear@22
|
563 {
|
nuclear@22
|
564 node->next = node_pool;
|
nuclear@22
|
565 node_pool = node;
|
nuclear@47
|
566 /*printf("free_node\n");*/
|
nuclear@22
|
567 }
|
nuclear@23
|
568
|
nuclear@47
|
569 /* clone_vm makes a copy of the current page tables, thus duplicating the
|
nuclear@47
|
570 * virtual address space.
|
nuclear@47
|
571 *
|
nuclear@47
|
572 * For the kernel part of the address space (last 256 page directory entries)
|
nuclear@47
|
573 * we don't want to diplicate the page tables, just point all page directory
|
nuclear@47
|
574 * entries to the same set of page tables.
|
nuclear@43
|
575 *
|
nuclear@57
|
576 * If "cow" is non-zero it also marks the shared user-space pages as
|
nuclear@57
|
577 * read-only, to implement copy-on-write.
|
nuclear@57
|
578 *
|
nuclear@43
|
579 * Returns the physical address of the new page directory.
|
nuclear@43
|
580 */
|
nuclear@57
|
581 uint32_t clone_vm(int cow)
|
nuclear@43
|
582 {
|
nuclear@57
|
583 int i, j, dirpg, tblpg, kstart_dirent;
|
nuclear@43
|
584 uint32_t paddr;
|
nuclear@43
|
585 uint32_t *ndir, *ntbl;
|
nuclear@43
|
586
|
nuclear@47
|
587 /* allocate the new page directory */
|
nuclear@43
|
588 if((dirpg = pgalloc(1, MEM_KERNEL)) == -1) {
|
nuclear@43
|
589 panic("clone_vmem: failed to allocate page directory page\n");
|
nuclear@43
|
590 }
|
nuclear@43
|
591 ndir = (uint32_t*)PAGE_TO_ADDR(dirpg);
|
nuclear@43
|
592
|
nuclear@47
|
593 /* allocate a virtual page for temporarily mapping all new
|
nuclear@47
|
594 * page tables while we populate them.
|
nuclear@47
|
595 */
|
nuclear@43
|
596 if((tblpg = pgalloc(1, MEM_KERNEL)) == -1) {
|
nuclear@43
|
597 panic("clone_vmem: failed to allocate page table page\n");
|
nuclear@43
|
598 }
|
nuclear@43
|
599 ntbl = (uint32_t*)PAGE_TO_ADDR(tblpg);
|
nuclear@43
|
600
|
nuclear@43
|
601 /* we will allocate physical pages and map them to this virtual page
|
nuclear@57
|
602 * as needed in the loop below. we don't need the physical page allocated
|
nuclear@57
|
603 * by pgalloc.
|
nuclear@43
|
604 */
|
nuclear@49
|
605 free_phys_page(virt_to_phys((uint32_t)ntbl));
|
nuclear@43
|
606
|
nuclear@48
|
607 kstart_dirent = ADDR_TO_PAGE(KMEM_START) / 1024;
|
nuclear@47
|
608
|
nuclear@47
|
609 /* user space */
|
nuclear@48
|
610 for(i=0; i<kstart_dirent; i++) {
|
nuclear@43
|
611 if(pgdir[i] & PG_PRESENT) {
|
nuclear@57
|
612 /* first go through all the entries of the existing
|
nuclear@57
|
613 * page table and unset the writable bits.
|
nuclear@57
|
614 */
|
nuclear@57
|
615 for(j=0; j<1024; j++) {
|
nuclear@57
|
616 PGTBL(i)[j] &= ~(uint32_t)PG_WRITABLE;
|
nuclear@57
|
617 }
|
nuclear@57
|
618
|
nuclear@57
|
619 /* allocate a page table for the clone */
|
nuclear@43
|
620 paddr = alloc_phys_page();
|
nuclear@43
|
621
|
nuclear@43
|
622 /* copy the page table */
|
nuclear@57
|
623 map_page(tblpg, ADDR_TO_PAGE(paddr), 0);
|
nuclear@43
|
624 memcpy(ntbl, PGTBL(i), PGSIZE);
|
nuclear@43
|
625
|
nuclear@43
|
626 /* set the new page directory entry */
|
nuclear@43
|
627 ndir[i] = paddr | (pgdir[i] & PGOFFS_MASK);
|
nuclear@43
|
628 } else {
|
nuclear@43
|
629 ndir[i] = 0;
|
nuclear@43
|
630 }
|
nuclear@43
|
631 }
|
nuclear@43
|
632
|
nuclear@55
|
633 /* for the kernel space we'll just use the same page tables */
|
nuclear@48
|
634 for(i=kstart_dirent; i<1024; i++) {
|
nuclear@49
|
635 ndir[i] = pgdir[i];
|
nuclear@47
|
636 }
|
nuclear@47
|
637
|
nuclear@57
|
638 /* we just changed all the page protection bits, so we need to flush the TLB */
|
nuclear@57
|
639 flush_tlb();
|
nuclear@57
|
640
|
nuclear@49
|
641 paddr = virt_to_phys((uint32_t)ndir);
|
nuclear@43
|
642
|
nuclear@57
|
643 /* unmap before freeing the virtual pages, to avoid deallocating the physical pages */
|
nuclear@43
|
644 unmap_page(dirpg);
|
nuclear@43
|
645 unmap_page(tblpg);
|
nuclear@43
|
646
|
nuclear@43
|
647 pgfree(dirpg, 1);
|
nuclear@43
|
648 pgfree(tblpg, 1);
|
nuclear@43
|
649
|
nuclear@43
|
650 return paddr;
|
nuclear@43
|
651 }
|
nuclear@57
|
652
|
nuclear@57
|
653 int get_page_bit(int pgnum, uint32_t bit, int wholepath)
|
nuclear@57
|
654 {
|
nuclear@57
|
655 int tidx = PAGE_TO_PGTBL(pgnum);
|
nuclear@57
|
656 int tent = PAGE_TO_PGTBL_PG(pgnum);
|
nuclear@57
|
657 uint32_t *pgtbl = PGTBL(tidx);
|
nuclear@57
|
658
|
nuclear@57
|
659 if(wholepath) {
|
nuclear@57
|
660 if((pgdir[tidx] & bit) == 0) {
|
nuclear@57
|
661 return 0;
|
nuclear@57
|
662 }
|
nuclear@57
|
663 }
|
nuclear@57
|
664
|
nuclear@57
|
665 return pgtbl[tent] & bit;
|
nuclear@57
|
666 }
|
nuclear@57
|
667
|
nuclear@57
|
668 void set_page_bit(int pgnum, uint32_t bit, int wholepath)
|
nuclear@57
|
669 {
|
nuclear@57
|
670 int tidx = PAGE_TO_PGTBL(pgnum);
|
nuclear@57
|
671 int tent = PAGE_TO_PGTBL_PG(pgnum);
|
nuclear@57
|
672 uint32_t *pgtbl = PGTBL(tidx);
|
nuclear@57
|
673
|
nuclear@57
|
674 if(wholepath) {
|
nuclear@57
|
675 pgdir[tidx] |= bit;
|
nuclear@57
|
676 }
|
nuclear@57
|
677 pgtbl[tent] |= bit;
|
nuclear@57
|
678
|
nuclear@57
|
679 flush_tlb_page(pgnum);
|
nuclear@57
|
680 }
|
nuclear@57
|
681
|
nuclear@57
|
682 void clear_page_bit(int pgnum, uint32_t bit, int wholepath)
|
nuclear@57
|
683 {
|
nuclear@57
|
684 int tidx = PAGE_TO_PGTBL(pgnum);
|
nuclear@57
|
685 int tent = PAGE_TO_PGTBL_PG(pgnum);
|
nuclear@57
|
686 uint32_t *pgtbl = PGTBL(tidx);
|
nuclear@57
|
687
|
nuclear@57
|
688 if(wholepath) {
|
nuclear@57
|
689 pgdir[tidx] &= ~bit;
|
nuclear@57
|
690 }
|
nuclear@57
|
691
|
nuclear@57
|
692 pgtbl[tent] &= ~bit;
|
nuclear@57
|
693
|
nuclear@57
|
694 flush_tlb_page(pgnum);
|
nuclear@57
|
695 }
|
nuclear@43
|
696
|
nuclear@43
|
697
|
nuclear@23
|
698 void dbg_print_vm(int area)
|
nuclear@23
|
699 {
|
nuclear@25
|
700 struct page_range *node;
|
nuclear@25
|
701 int last, intr_state;
|
nuclear@25
|
702
|
nuclear@25
|
703 intr_state = get_intr_state();
|
nuclear@25
|
704 disable_intr();
|
nuclear@25
|
705
|
nuclear@25
|
706 node = pglist[area];
|
nuclear@25
|
707 last = area == MEM_USER ? 0 : ADDR_TO_PAGE(KMEM_START);
|
nuclear@23
|
708
|
nuclear@23
|
709 printf("%s vm space\n", area == MEM_USER ? "user" : "kernel");
|
nuclear@23
|
710
|
nuclear@23
|
711 while(node) {
|
nuclear@23
|
712 if(node->start > last) {
|
nuclear@23
|
713 printf(" vm-used: %x -> %x\n", PAGE_TO_ADDR(last), PAGE_TO_ADDR(node->start));
|
nuclear@23
|
714 }
|
nuclear@23
|
715
|
nuclear@23
|
716 printf(" vm-free: %x -> ", PAGE_TO_ADDR(node->start));
|
nuclear@23
|
717 if(node->end >= PAGE_COUNT) {
|
nuclear@23
|
718 printf("END\n");
|
nuclear@23
|
719 } else {
|
nuclear@23
|
720 printf("%x\n", PAGE_TO_ADDR(node->end));
|
nuclear@23
|
721 }
|
nuclear@23
|
722
|
nuclear@23
|
723 last = node->end;
|
nuclear@23
|
724 node = node->next;
|
nuclear@23
|
725 }
|
nuclear@25
|
726
|
nuclear@25
|
727 set_intr_state(intr_state);
|
nuclear@23
|
728 }
|