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