rev |
line source |
nuclear@17
|
1 #include <stdio.h>
|
nuclear@17
|
2 #include <string.h>
|
nuclear@17
|
3 #include <inttypes.h>
|
nuclear@17
|
4 #include "vm.h"
|
nuclear@17
|
5 #include <stdio.h>
|
nuclear@17
|
6 #include "intr.h"
|
nuclear@17
|
7 #include "mem.h"
|
nuclear@17
|
8 #include "panic.h"
|
nuclear@17
|
9
|
nuclear@17
|
10
|
nuclear@17
|
11 #define KMEM_START 0xc0000000
|
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@22
|
41 static void pgfault(int inum, uint32_t err);
|
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@19
|
59
|
nuclear@23
|
60 /* setup the page tables */
|
nuclear@18
|
61 pgdir = (uint32_t*)alloc_phys_page();
|
nuclear@23
|
62 memset(pgdir, 0, PGSIZE);
|
nuclear@24
|
63 set_pgdir_addr((uint32_t)pgdir);
|
nuclear@17
|
64
|
nuclear@17
|
65 /* map the video memory and kernel code 1-1 */
|
nuclear@19
|
66 get_kernel_mem_range(0, &idmap_end);
|
nuclear@19
|
67 map_mem_range(IDMAP_START, idmap_end - IDMAP_START, IDMAP_START, 0);
|
nuclear@17
|
68
|
nuclear@24
|
69 /* make the last page directory entry point to the page directory */
|
nuclear@24
|
70 pgdir[1023] = ((uint32_t)pgdir & ADDR_PGENT_MASK) | PG_PRESENT;
|
nuclear@24
|
71 pgdir = (uint32_t*)PGDIR_ADDR;
|
nuclear@24
|
72
|
nuclear@23
|
73 /* set the page fault handler */
|
nuclear@17
|
74 interrupt(PAGEFAULT, pgfault);
|
nuclear@17
|
75
|
nuclear@23
|
76 /* we can enable paging now */
|
nuclear@17
|
77 enable_paging();
|
nuclear@23
|
78
|
nuclear@23
|
79 /* initialize the virtual page allocator */
|
nuclear@23
|
80 node_pool = 0;
|
nuclear@23
|
81
|
nuclear@23
|
82 first_node.start = ADDR_TO_PAGE(KMEM_START);
|
nuclear@26
|
83 first_node.end = ADDR_TO_PAGE(PGTBL_BASE);
|
nuclear@23
|
84 first_node.next = 0;
|
nuclear@23
|
85 pglist[MEM_KERNEL] = &first_node;
|
nuclear@23
|
86
|
nuclear@23
|
87 pglist[MEM_USER] = alloc_node();
|
nuclear@26
|
88 pglist[MEM_USER]->start = ADDR_TO_PAGE(idmap_end);
|
nuclear@23
|
89 pglist[MEM_USER]->end = ADDR_TO_PAGE(KMEM_START);
|
nuclear@23
|
90 pglist[MEM_USER]->next = 0;
|
nuclear@17
|
91 }
|
nuclear@17
|
92
|
nuclear@23
|
93 /* if ppage == -1 we allocate a physical page by calling alloc_phys_page */
|
nuclear@23
|
94 int map_page(int vpage, int ppage, unsigned int attr)
|
nuclear@17
|
95 {
|
nuclear@17
|
96 uint32_t *pgtbl;
|
nuclear@25
|
97 int diridx, pgidx, pgon, intr_state;
|
nuclear@25
|
98
|
nuclear@25
|
99 intr_state = get_intr_state();
|
nuclear@25
|
100 disable_intr();
|
nuclear@23
|
101
|
nuclear@23
|
102 pgon = get_paging_status();
|
nuclear@23
|
103
|
nuclear@23
|
104 if(ppage < 0) {
|
nuclear@23
|
105 uint32_t addr = alloc_phys_page();
|
nuclear@23
|
106 if(!addr) {
|
nuclear@25
|
107 set_intr_state(intr_state);
|
nuclear@23
|
108 return -1;
|
nuclear@23
|
109 }
|
nuclear@23
|
110 ppage = ADDR_TO_PAGE(addr);
|
nuclear@23
|
111 }
|
nuclear@23
|
112
|
nuclear@23
|
113 diridx = PAGE_TO_PGTBL(vpage);
|
nuclear@23
|
114 pgidx = PAGE_TO_PGTBL_PG(vpage);
|
nuclear@17
|
115
|
nuclear@17
|
116 if(!(pgdir[diridx] & PG_PRESENT)) {
|
nuclear@17
|
117 uint32_t addr = alloc_phys_page();
|
nuclear@24
|
118 pgdir[diridx] = addr | (attr & ATTR_PGDIR_MASK) | PG_PRESENT;
|
nuclear@24
|
119
|
nuclear@24
|
120 pgtbl = pgon ? PGTBL(diridx) : (uint32_t*)addr;
|
nuclear@18
|
121 memset(pgtbl, 0, PGSIZE);
|
nuclear@17
|
122 } else {
|
nuclear@24
|
123 if(pgon) {
|
nuclear@24
|
124 pgtbl = PGTBL(diridx);
|
nuclear@24
|
125 } else {
|
nuclear@24
|
126 pgtbl = (uint32_t*)(pgdir[diridx] & ADDR_PGENT_MASK);
|
nuclear@24
|
127 }
|
nuclear@17
|
128 }
|
nuclear@17
|
129
|
nuclear@17
|
130 pgtbl[pgidx] = PAGE_TO_ADDR(ppage) | (attr & ATTR_PGTBL_MASK) | PG_PRESENT;
|
nuclear@23
|
131 flush_tlb_page(vpage);
|
nuclear@23
|
132
|
nuclear@25
|
133 set_intr_state(intr_state);
|
nuclear@23
|
134 return 0;
|
nuclear@17
|
135 }
|
nuclear@17
|
136
|
nuclear@43
|
137 int unmap_page(int vpage)
|
nuclear@17
|
138 {
|
nuclear@17
|
139 uint32_t *pgtbl;
|
nuclear@43
|
140 int res = 0;
|
nuclear@17
|
141 int diridx = PAGE_TO_PGTBL(vpage);
|
nuclear@17
|
142 int pgidx = PAGE_TO_PGTBL_PG(vpage);
|
nuclear@17
|
143
|
nuclear@25
|
144 int intr_state = get_intr_state();
|
nuclear@25
|
145 disable_intr();
|
nuclear@25
|
146
|
nuclear@17
|
147 if(!(pgdir[diridx] & PG_PRESENT)) {
|
nuclear@17
|
148 goto err;
|
nuclear@17
|
149 }
|
nuclear@26
|
150 pgtbl = PGTBL(diridx);
|
nuclear@17
|
151
|
nuclear@17
|
152 if(!(pgtbl[pgidx] & PG_PRESENT)) {
|
nuclear@17
|
153 goto err;
|
nuclear@17
|
154 }
|
nuclear@17
|
155 pgtbl[pgidx] = 0;
|
nuclear@23
|
156 flush_tlb_page(vpage);
|
nuclear@17
|
157
|
nuclear@25
|
158 if(0) {
|
nuclear@17
|
159 err:
|
nuclear@25
|
160 printf("unmap_page(%d): page already not mapped\n", vpage);
|
nuclear@43
|
161 res = -1;
|
nuclear@25
|
162 }
|
nuclear@25
|
163 set_intr_state(intr_state);
|
nuclear@43
|
164 return res;
|
nuclear@17
|
165 }
|
nuclear@17
|
166
|
nuclear@22
|
167 /* if ppg_start is -1, we allocate physical pages to map with alloc_phys_page() */
|
nuclear@23
|
168 int map_page_range(int vpg_start, int pgcount, int ppg_start, unsigned int attr)
|
nuclear@17
|
169 {
|
nuclear@23
|
170 int i, phys_pg;
|
nuclear@17
|
171
|
nuclear@17
|
172 for(i=0; i<pgcount; i++) {
|
nuclear@26
|
173 phys_pg = ppg_start < 0 ? -1 : ppg_start + i;
|
nuclear@23
|
174 map_page(vpg_start + i, phys_pg, attr);
|
nuclear@17
|
175 }
|
nuclear@23
|
176 return 0;
|
nuclear@17
|
177 }
|
nuclear@17
|
178
|
nuclear@43
|
179 int unmap_page_range(int vpg_start, int pgcount)
|
nuclear@43
|
180 {
|
nuclear@43
|
181 int i, res = 0;
|
nuclear@43
|
182
|
nuclear@43
|
183 for(i=0; i<pgcount; i++) {
|
nuclear@43
|
184 if(unmap_page(vpg_start + i) == -1) {
|
nuclear@43
|
185 res = -1;
|
nuclear@43
|
186 }
|
nuclear@43
|
187 }
|
nuclear@43
|
188 return res;
|
nuclear@43
|
189 }
|
nuclear@43
|
190
|
nuclear@23
|
191 /* if paddr is 0, we allocate physical pages with alloc_phys_page() */
|
nuclear@23
|
192 int map_mem_range(uint32_t vaddr, size_t sz, uint32_t paddr, unsigned int attr)
|
nuclear@17
|
193 {
|
nuclear@17
|
194 int vpg_start, ppg_start, num_pages;
|
nuclear@17
|
195
|
nuclear@23
|
196 if(!sz) return -1;
|
nuclear@17
|
197
|
nuclear@17
|
198 if(ADDR_TO_PGOFFS(paddr)) {
|
nuclear@17
|
199 panic("map_mem_range called with unaligned physical address: %x\n", paddr);
|
nuclear@17
|
200 }
|
nuclear@17
|
201
|
nuclear@17
|
202 vpg_start = ADDR_TO_PAGE(vaddr);
|
nuclear@23
|
203 ppg_start = paddr > 0 ? ADDR_TO_PAGE(paddr) : -1;
|
nuclear@17
|
204 num_pages = ADDR_TO_PAGE(sz) + 1;
|
nuclear@17
|
205
|
nuclear@23
|
206 return map_page_range(vpg_start, num_pages, ppg_start, attr);
|
nuclear@17
|
207 }
|
nuclear@17
|
208
|
nuclear@18
|
209 uint32_t virt_to_phys(uint32_t vaddr)
|
nuclear@18
|
210 {
|
nuclear@43
|
211 int pg;
|
nuclear@43
|
212 uint32_t pgaddr;
|
nuclear@43
|
213
|
nuclear@43
|
214 if((pg = virt_to_phys_page(ADDR_TO_PAGE(vaddr))) == -1) {
|
nuclear@43
|
215 return 0;
|
nuclear@43
|
216 }
|
nuclear@43
|
217 pgaddr = PAGE_TO_ADDR(pg);
|
nuclear@43
|
218
|
nuclear@43
|
219 return pgaddr | ADDR_TO_PGOFFS(vaddr);
|
nuclear@43
|
220 }
|
nuclear@43
|
221
|
nuclear@43
|
222 int virt_to_phys_page(int vpg)
|
nuclear@43
|
223 {
|
nuclear@18
|
224 uint32_t pgaddr, *pgtbl;
|
nuclear@43
|
225 int diridx, pgidx;
|
nuclear@43
|
226
|
nuclear@43
|
227 if(vpg < 0 || vpg >= PAGE_COUNT) {
|
nuclear@43
|
228 return -1;
|
nuclear@43
|
229 }
|
nuclear@43
|
230
|
nuclear@43
|
231 diridx = PAGE_TO_PGTBL(vpg);
|
nuclear@43
|
232 pgidx = PAGE_TO_PGTBL_PG(vpg);
|
nuclear@18
|
233
|
nuclear@18
|
234 if(!(pgdir[diridx] & PG_PRESENT)) {
|
nuclear@43
|
235 return -1;
|
nuclear@18
|
236 }
|
nuclear@26
|
237 pgtbl = PGTBL(diridx);
|
nuclear@18
|
238
|
nuclear@18
|
239 if(!(pgtbl[pgidx] & PG_PRESENT)) {
|
nuclear@43
|
240 return -1;
|
nuclear@18
|
241 }
|
nuclear@18
|
242 pgaddr = pgtbl[pgidx] & PGENT_ADDR_MASK;
|
nuclear@43
|
243 return ADDR_TO_PAGE(pgaddr);
|
nuclear@18
|
244 }
|
nuclear@18
|
245
|
nuclear@22
|
246 /* allocate a contiguous block of virtual memory pages along with
|
nuclear@22
|
247 * backing physical memory for them, and update the page table.
|
nuclear@22
|
248 */
|
nuclear@22
|
249 int pgalloc(int num, int area)
|
nuclear@22
|
250 {
|
nuclear@25
|
251 int intr_state, ret = -1;
|
nuclear@22
|
252 struct page_range *node, *prev, dummy;
|
nuclear@45
|
253 unsigned int attr = 0; /* TODO */
|
nuclear@22
|
254
|
nuclear@25
|
255 intr_state = get_intr_state();
|
nuclear@25
|
256 disable_intr();
|
nuclear@25
|
257
|
nuclear@22
|
258 dummy.next = pglist[area];
|
nuclear@22
|
259 node = pglist[area];
|
nuclear@22
|
260 prev = &dummy;
|
nuclear@22
|
261
|
nuclear@22
|
262 while(node) {
|
nuclear@22
|
263 if(node->end - node->start >= num) {
|
nuclear@22
|
264 ret = node->start;
|
nuclear@22
|
265 node->start += num;
|
nuclear@22
|
266
|
nuclear@22
|
267 if(node->start == node->end) {
|
nuclear@22
|
268 prev->next = node->next;
|
nuclear@22
|
269 node->next = 0;
|
nuclear@22
|
270
|
nuclear@22
|
271 if(node == pglist[area]) {
|
nuclear@22
|
272 pglist[area] = 0;
|
nuclear@22
|
273 }
|
nuclear@22
|
274 free_node(node);
|
nuclear@22
|
275 }
|
nuclear@22
|
276 break;
|
nuclear@22
|
277 }
|
nuclear@22
|
278
|
nuclear@22
|
279 prev = node;
|
nuclear@22
|
280 node = node->next;
|
nuclear@22
|
281 }
|
nuclear@22
|
282
|
nuclear@22
|
283 if(ret >= 0) {
|
nuclear@23
|
284 /* allocate physical storage and map */
|
nuclear@45
|
285 if(map_page_range(ret, num, -1, attr) == -1) {
|
nuclear@45
|
286 ret = -1;
|
nuclear@45
|
287 }
|
nuclear@45
|
288 }
|
nuclear@45
|
289
|
nuclear@45
|
290 set_intr_state(intr_state);
|
nuclear@45
|
291 return ret;
|
nuclear@45
|
292 }
|
nuclear@45
|
293
|
nuclear@45
|
294 int pgalloc_vrange(int start, int num)
|
nuclear@45
|
295 {
|
nuclear@45
|
296 struct page_range *node, *prev, dummy;
|
nuclear@45
|
297 int area, intr_state, ret = -1;
|
nuclear@45
|
298 unsigned int attr = 0; /* TODO */
|
nuclear@45
|
299
|
nuclear@45
|
300 area = (start >= ADDR_TO_PAGE(KMEM_START)) ? MEM_KERNEL : MEM_USER;
|
nuclear@45
|
301 if(area == KMEM_USER && start + num > ADDR_TO_PAGE(KMEM_START)) {
|
nuclear@45
|
302 printf("pgalloc_vrange: invalid range request crossing user/kernel split\n");
|
nuclear@45
|
303 return -1;
|
nuclear@45
|
304 }
|
nuclear@45
|
305
|
nuclear@45
|
306 intr_state = get_intr_state();
|
nuclear@45
|
307 disable_intr();
|
nuclear@45
|
308
|
nuclear@45
|
309 dummy.next = pglist[area];
|
nuclear@45
|
310 node = pglist[area];
|
nuclear@45
|
311 prev = &dummy;
|
nuclear@45
|
312
|
nuclear@45
|
313 /* check to see if the requested VM range is available */
|
nuclear@45
|
314 node = pglist[area];
|
nuclear@45
|
315 while(node) {
|
nuclear@45
|
316 if(start >= node->start && start + num <= node->end) {
|
nuclear@45
|
317 ret = node->start;
|
nuclear@45
|
318 node->start += num;
|
nuclear@45
|
319
|
nuclear@45
|
320 if(node->start == node->end) {
|
nuclear@45
|
321 prev->next = node->next;
|
nuclear@45
|
322 node->next = 0;
|
nuclear@45
|
323
|
nuclear@45
|
324 if(node == pglist[area]) {
|
nuclear@45
|
325 pglist[area] = 0;
|
nuclear@45
|
326 }
|
nuclear@45
|
327 free_node(node);
|
nuclear@45
|
328 }
|
nuclear@45
|
329 break;
|
nuclear@45
|
330 }
|
nuclear@45
|
331
|
nuclear@45
|
332 prev = node;
|
nuclear@45
|
333 node = node->next;
|
nuclear@45
|
334 }
|
nuclear@45
|
335
|
nuclear@45
|
336 if(ret >= 0) {
|
nuclear@45
|
337 /* allocate physical storage and map */
|
nuclear@45
|
338 if(map_page_range(ret, num, -1, attr) == -1) {
|
nuclear@23
|
339 ret = -1;
|
nuclear@23
|
340 }
|
nuclear@22
|
341 }
|
nuclear@22
|
342
|
nuclear@25
|
343 set_intr_state(intr_state);
|
nuclear@22
|
344 return ret;
|
nuclear@22
|
345 }
|
nuclear@22
|
346
|
nuclear@22
|
347 void pgfree(int start, int num)
|
nuclear@22
|
348 {
|
nuclear@33
|
349 int i, area, intr_state;
|
nuclear@23
|
350 struct page_range *node, *new, *prev, *next;
|
nuclear@23
|
351
|
nuclear@25
|
352 intr_state = get_intr_state();
|
nuclear@25
|
353 disable_intr();
|
nuclear@25
|
354
|
nuclear@26
|
355 for(i=0; i<num; i++) {
|
nuclear@43
|
356 int phys_pg = virt_to_phys_page(start + i);
|
nuclear@43
|
357 if(phys_pg != -1) {
|
nuclear@43
|
358 free_phys_page(phys_pg);
|
nuclear@26
|
359 }
|
nuclear@26
|
360 }
|
nuclear@26
|
361
|
nuclear@23
|
362 if(!(new = alloc_node())) {
|
nuclear@23
|
363 panic("pgfree: can't allocate new page_range node to add the freed pages\n");
|
nuclear@23
|
364 }
|
nuclear@23
|
365 new->start = start;
|
nuclear@33
|
366 new->end = start + num;
|
nuclear@23
|
367
|
nuclear@23
|
368 area = PAGE_TO_ADDR(start) >= KMEM_START ? MEM_KERNEL : MEM_USER;
|
nuclear@23
|
369
|
nuclear@23
|
370 if(!pglist[area] || pglist[area]->start > start) {
|
nuclear@23
|
371 next = new->next = pglist[area];
|
nuclear@23
|
372 pglist[area] = new;
|
nuclear@23
|
373 prev = 0;
|
nuclear@23
|
374
|
nuclear@23
|
375 } else {
|
nuclear@23
|
376
|
nuclear@23
|
377 prev = 0;
|
nuclear@23
|
378 node = pglist[area];
|
nuclear@23
|
379 next = node ? node->next : 0;
|
nuclear@23
|
380
|
nuclear@23
|
381 while(node) {
|
nuclear@23
|
382 if(!next || next->start > start) {
|
nuclear@23
|
383 /* place here, after node */
|
nuclear@23
|
384 new->next = next;
|
nuclear@23
|
385 node->next = new;
|
nuclear@23
|
386 prev = node; /* needed by coalesce after the loop */
|
nuclear@23
|
387 break;
|
nuclear@23
|
388 }
|
nuclear@23
|
389
|
nuclear@23
|
390 prev = node;
|
nuclear@23
|
391 node = next;
|
nuclear@23
|
392 next = node ? node->next : 0;
|
nuclear@23
|
393 }
|
nuclear@23
|
394 }
|
nuclear@23
|
395
|
nuclear@23
|
396 coalesce(prev, new, next);
|
nuclear@25
|
397 set_intr_state(intr_state);
|
nuclear@23
|
398 }
|
nuclear@23
|
399
|
nuclear@23
|
400 static void coalesce(struct page_range *low, struct page_range *mid, struct page_range *high)
|
nuclear@23
|
401 {
|
nuclear@23
|
402 if(high) {
|
nuclear@23
|
403 if(mid->end == high->start) {
|
nuclear@23
|
404 mid->end = high->end;
|
nuclear@23
|
405 mid->next = high->next;
|
nuclear@23
|
406 free_node(high);
|
nuclear@23
|
407 }
|
nuclear@23
|
408 }
|
nuclear@23
|
409
|
nuclear@23
|
410 if(low) {
|
nuclear@23
|
411 if(low->end == mid->start) {
|
nuclear@23
|
412 low->end += mid->end;
|
nuclear@23
|
413 low->next = mid->next;
|
nuclear@23
|
414 free_node(mid);
|
nuclear@23
|
415 }
|
nuclear@23
|
416 }
|
nuclear@22
|
417 }
|
nuclear@22
|
418
|
nuclear@17
|
419 static void pgfault(int inum, uint32_t err)
|
nuclear@17
|
420 {
|
nuclear@17
|
421 printf("~~~~ PAGE FAULT ~~~~\n");
|
nuclear@17
|
422
|
nuclear@17
|
423 printf("fault address: %x\n", get_fault_addr());
|
nuclear@17
|
424
|
nuclear@17
|
425 if(err & PG_PRESENT) {
|
nuclear@17
|
426 if(err & 8) {
|
nuclear@17
|
427 printf("reserved bit set in some paging structure\n");
|
nuclear@17
|
428 } else {
|
nuclear@17
|
429 printf("%s protection violation ", (err & PG_WRITABLE) ? "write" : "read");
|
nuclear@17
|
430 printf("in %s mode\n", err & PG_USER ? "user" : "kernel");
|
nuclear@17
|
431 }
|
nuclear@17
|
432 } else {
|
nuclear@17
|
433 printf("page not present\n");
|
nuclear@17
|
434 }
|
nuclear@19
|
435
|
nuclear@19
|
436 panic("unhandled page fault\n");
|
nuclear@17
|
437 }
|
nuclear@22
|
438
|
nuclear@22
|
439 /* --- page range list node management --- */
|
nuclear@23
|
440 #define NODES_IN_PAGE (PGSIZE / sizeof(struct page_range))
|
nuclear@23
|
441
|
nuclear@22
|
442 static struct page_range *alloc_node(void)
|
nuclear@22
|
443 {
|
nuclear@22
|
444 struct page_range *node;
|
nuclear@23
|
445 int pg, i;
|
nuclear@22
|
446
|
nuclear@22
|
447 if(node_pool) {
|
nuclear@22
|
448 node = node_pool;
|
nuclear@22
|
449 node_pool = node_pool->next;
|
nuclear@23
|
450 printf("alloc_node -> %x\n", (unsigned int)node);
|
nuclear@22
|
451 return node;
|
nuclear@22
|
452 }
|
nuclear@22
|
453
|
nuclear@23
|
454 /* no node structures in the pool, we need to allocate a new page,
|
nuclear@23
|
455 * split it up into node structures, add them in the pool, and
|
nuclear@23
|
456 * allocate one of them.
|
nuclear@22
|
457 */
|
nuclear@23
|
458 if(!(pg = pgalloc(1, MEM_KERNEL))) {
|
nuclear@22
|
459 panic("ran out of physical memory while allocating VM range structures\n");
|
nuclear@22
|
460 }
|
nuclear@23
|
461 node_pool = (struct page_range*)PAGE_TO_ADDR(pg);
|
nuclear@22
|
462
|
nuclear@23
|
463 /* link them up, skip the first as we'll just allocate it anyway */
|
nuclear@23
|
464 for(i=2; i<NODES_IN_PAGE; i++) {
|
nuclear@23
|
465 node_pool[i - 1].next = node_pool + i;
|
nuclear@23
|
466 }
|
nuclear@23
|
467 node_pool[NODES_IN_PAGE - 1].next = 0;
|
nuclear@23
|
468
|
nuclear@23
|
469 /* grab the first and return it */
|
nuclear@23
|
470 node = node_pool++;
|
nuclear@23
|
471 printf("alloc_node -> %x\n", (unsigned int)node);
|
nuclear@23
|
472 return node;
|
nuclear@22
|
473 }
|
nuclear@22
|
474
|
nuclear@22
|
475 static void free_node(struct page_range *node)
|
nuclear@22
|
476 {
|
nuclear@22
|
477 node->next = node_pool;
|
nuclear@22
|
478 node_pool = node;
|
nuclear@23
|
479 printf("free_node\n");
|
nuclear@22
|
480 }
|
nuclear@23
|
481
|
nuclear@23
|
482
|
nuclear@43
|
483 /* clone_vmem makes a copy of the current page tables, thus duplicating
|
nuclear@43
|
484 * the virtual address space.
|
nuclear@43
|
485 *
|
nuclear@43
|
486 * Returns the physical address of the new page directory.
|
nuclear@43
|
487 */
|
nuclear@43
|
488 uint32_t clone_vmem(void)
|
nuclear@43
|
489 {
|
nuclear@43
|
490 int i, dirpg, tblpg;
|
nuclear@43
|
491 uint32_t paddr;
|
nuclear@43
|
492 uint32_t *ndir, *ntbl;
|
nuclear@43
|
493
|
nuclear@43
|
494 if((dirpg = pgalloc(1, MEM_KERNEL)) == -1) {
|
nuclear@43
|
495 panic("clone_vmem: failed to allocate page directory page\n");
|
nuclear@43
|
496 }
|
nuclear@43
|
497 ndir = (uint32_t*)PAGE_TO_ADDR(dirpg);
|
nuclear@43
|
498
|
nuclear@43
|
499 if((tblpg = pgalloc(1, MEM_KERNEL)) == -1) {
|
nuclear@43
|
500 panic("clone_vmem: failed to allocate page table page\n");
|
nuclear@43
|
501 }
|
nuclear@43
|
502 ntbl = (uint32_t*)PAGE_TO_ADDR(tblpg);
|
nuclear@43
|
503
|
nuclear@43
|
504 /* we will allocate physical pages and map them to this virtual page
|
nuclear@43
|
505 * as needed in the loop below.
|
nuclear@43
|
506 */
|
nuclear@43
|
507 free_phys_page(virt_to_phys(tblpg));
|
nuclear@43
|
508
|
nuclear@43
|
509 for(i=0; i<1024; i++) {
|
nuclear@43
|
510 if(pgdir[i] & PG_PRESENT) {
|
nuclear@43
|
511 paddr = alloc_phys_page();
|
nuclear@43
|
512 map_page(tblpg, ADDR_TO_PAGE(paddr), 0);
|
nuclear@43
|
513
|
nuclear@43
|
514 /* copy the page table */
|
nuclear@43
|
515 memcpy(ntbl, PGTBL(i), PGSIZE);
|
nuclear@43
|
516
|
nuclear@43
|
517 /* set the new page directory entry */
|
nuclear@43
|
518 ndir[i] = paddr | (pgdir[i] & PGOFFS_MASK);
|
nuclear@43
|
519 } else {
|
nuclear@43
|
520 ndir[i] = 0;
|
nuclear@43
|
521 }
|
nuclear@43
|
522 }
|
nuclear@43
|
523
|
nuclear@43
|
524 paddr = virt_to_phys(dirpg);
|
nuclear@43
|
525
|
nuclear@43
|
526 /* unmap before freeing to avoid deallocating the physical pages */
|
nuclear@43
|
527 unmap_page(dirpg);
|
nuclear@43
|
528 unmap_page(tblpg);
|
nuclear@43
|
529
|
nuclear@43
|
530 pgfree(dirpg, 1);
|
nuclear@43
|
531 pgfree(tblpg, 1);
|
nuclear@43
|
532
|
nuclear@43
|
533 return paddr;
|
nuclear@43
|
534 }
|
nuclear@43
|
535
|
nuclear@43
|
536
|
nuclear@23
|
537 void dbg_print_vm(int area)
|
nuclear@23
|
538 {
|
nuclear@25
|
539 struct page_range *node;
|
nuclear@25
|
540 int last, intr_state;
|
nuclear@25
|
541
|
nuclear@25
|
542 intr_state = get_intr_state();
|
nuclear@25
|
543 disable_intr();
|
nuclear@25
|
544
|
nuclear@25
|
545 node = pglist[area];
|
nuclear@25
|
546 last = area == MEM_USER ? 0 : ADDR_TO_PAGE(KMEM_START);
|
nuclear@23
|
547
|
nuclear@23
|
548 printf("%s vm space\n", area == MEM_USER ? "user" : "kernel");
|
nuclear@23
|
549
|
nuclear@23
|
550 while(node) {
|
nuclear@23
|
551 if(node->start > last) {
|
nuclear@23
|
552 printf(" vm-used: %x -> %x\n", PAGE_TO_ADDR(last), PAGE_TO_ADDR(node->start));
|
nuclear@23
|
553 }
|
nuclear@23
|
554
|
nuclear@23
|
555 printf(" vm-free: %x -> ", PAGE_TO_ADDR(node->start));
|
nuclear@23
|
556 if(node->end >= PAGE_COUNT) {
|
nuclear@23
|
557 printf("END\n");
|
nuclear@23
|
558 } else {
|
nuclear@23
|
559 printf("%x\n", PAGE_TO_ADDR(node->end));
|
nuclear@23
|
560 }
|
nuclear@23
|
561
|
nuclear@23
|
562 last = node->end;
|
nuclear@23
|
563 node = node->next;
|
nuclear@23
|
564 }
|
nuclear@25
|
565
|
nuclear@25
|
566 set_intr_state(intr_state);
|
nuclear@23
|
567 }
|