kern

annotate src/vm.c @ 48:4c9c16754b59

fixed the page fault
author John Tsiombikas <nuclear@member.fsf.org>
date Thu, 28 Jul 2011 17:56:05 +0300
parents f65b348780e3
children 50730d42d2d3
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@22 40 static void pgfault(int inum, uint32_t err);
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@45 331 ret = node->start;
nuclear@45 332 node->start += num;
nuclear@45 333
nuclear@45 334 if(node->start == node->end) {
nuclear@45 335 prev->next = node->next;
nuclear@45 336 node->next = 0;
nuclear@45 337
nuclear@45 338 if(node == pglist[area]) {
nuclear@45 339 pglist[area] = 0;
nuclear@45 340 }
nuclear@45 341 free_node(node);
nuclear@45 342 }
nuclear@45 343 break;
nuclear@45 344 }
nuclear@45 345
nuclear@45 346 prev = node;
nuclear@45 347 node = node->next;
nuclear@45 348 }
nuclear@45 349
nuclear@45 350 if(ret >= 0) {
nuclear@45 351 /* allocate physical storage and map */
nuclear@45 352 if(map_page_range(ret, num, -1, attr) == -1) {
nuclear@23 353 ret = -1;
nuclear@23 354 }
nuclear@22 355 }
nuclear@22 356
nuclear@25 357 set_intr_state(intr_state);
nuclear@22 358 return ret;
nuclear@22 359 }
nuclear@22 360
nuclear@22 361 void pgfree(int start, int num)
nuclear@22 362 {
nuclear@33 363 int i, area, intr_state;
nuclear@23 364 struct page_range *node, *new, *prev, *next;
nuclear@23 365
nuclear@25 366 intr_state = get_intr_state();
nuclear@25 367 disable_intr();
nuclear@25 368
nuclear@26 369 for(i=0; i<num; i++) {
nuclear@43 370 int phys_pg = virt_to_phys_page(start + i);
nuclear@43 371 if(phys_pg != -1) {
nuclear@43 372 free_phys_page(phys_pg);
nuclear@26 373 }
nuclear@26 374 }
nuclear@26 375
nuclear@23 376 if(!(new = alloc_node())) {
nuclear@23 377 panic("pgfree: can't allocate new page_range node to add the freed pages\n");
nuclear@23 378 }
nuclear@23 379 new->start = start;
nuclear@33 380 new->end = start + num;
nuclear@23 381
nuclear@23 382 area = PAGE_TO_ADDR(start) >= KMEM_START ? MEM_KERNEL : MEM_USER;
nuclear@23 383
nuclear@23 384 if(!pglist[area] || pglist[area]->start > start) {
nuclear@23 385 next = new->next = pglist[area];
nuclear@23 386 pglist[area] = new;
nuclear@23 387 prev = 0;
nuclear@23 388
nuclear@23 389 } else {
nuclear@23 390
nuclear@23 391 prev = 0;
nuclear@23 392 node = pglist[area];
nuclear@23 393 next = node ? node->next : 0;
nuclear@23 394
nuclear@23 395 while(node) {
nuclear@23 396 if(!next || next->start > start) {
nuclear@23 397 /* place here, after node */
nuclear@23 398 new->next = next;
nuclear@23 399 node->next = new;
nuclear@23 400 prev = node; /* needed by coalesce after the loop */
nuclear@23 401 break;
nuclear@23 402 }
nuclear@23 403
nuclear@23 404 prev = node;
nuclear@23 405 node = next;
nuclear@23 406 next = node ? node->next : 0;
nuclear@23 407 }
nuclear@23 408 }
nuclear@23 409
nuclear@23 410 coalesce(prev, new, next);
nuclear@25 411 set_intr_state(intr_state);
nuclear@23 412 }
nuclear@23 413
nuclear@23 414 static void coalesce(struct page_range *low, struct page_range *mid, struct page_range *high)
nuclear@23 415 {
nuclear@23 416 if(high) {
nuclear@23 417 if(mid->end == high->start) {
nuclear@23 418 mid->end = high->end;
nuclear@23 419 mid->next = high->next;
nuclear@23 420 free_node(high);
nuclear@23 421 }
nuclear@23 422 }
nuclear@23 423
nuclear@23 424 if(low) {
nuclear@23 425 if(low->end == mid->start) {
nuclear@23 426 low->end += mid->end;
nuclear@23 427 low->next = mid->next;
nuclear@23 428 free_node(mid);
nuclear@23 429 }
nuclear@23 430 }
nuclear@22 431 }
nuclear@22 432
nuclear@17 433 static void pgfault(int inum, uint32_t err)
nuclear@17 434 {
nuclear@17 435 printf("~~~~ PAGE FAULT ~~~~\n");
nuclear@17 436
nuclear@17 437 printf("fault address: %x\n", get_fault_addr());
nuclear@17 438
nuclear@17 439 if(err & PG_PRESENT) {
nuclear@17 440 if(err & 8) {
nuclear@17 441 printf("reserved bit set in some paging structure\n");
nuclear@17 442 } else {
nuclear@17 443 printf("%s protection violation ", (err & PG_WRITABLE) ? "write" : "read");
nuclear@17 444 printf("in %s mode\n", err & PG_USER ? "user" : "kernel");
nuclear@17 445 }
nuclear@17 446 } else {
nuclear@17 447 printf("page not present\n");
nuclear@17 448 }
nuclear@19 449
nuclear@19 450 panic("unhandled page fault\n");
nuclear@17 451 }
nuclear@22 452
nuclear@22 453 /* --- page range list node management --- */
nuclear@23 454 #define NODES_IN_PAGE (PGSIZE / sizeof(struct page_range))
nuclear@23 455
nuclear@22 456 static struct page_range *alloc_node(void)
nuclear@22 457 {
nuclear@22 458 struct page_range *node;
nuclear@23 459 int pg, i;
nuclear@22 460
nuclear@22 461 if(node_pool) {
nuclear@22 462 node = node_pool;
nuclear@22 463 node_pool = node_pool->next;
nuclear@47 464 /*printf("alloc_node -> %x\n", (unsigned int)node);*/
nuclear@22 465 return node;
nuclear@22 466 }
nuclear@22 467
nuclear@23 468 /* no node structures in the pool, we need to allocate a new page,
nuclear@23 469 * split it up into node structures, add them in the pool, and
nuclear@23 470 * allocate one of them.
nuclear@22 471 */
nuclear@23 472 if(!(pg = pgalloc(1, MEM_KERNEL))) {
nuclear@22 473 panic("ran out of physical memory while allocating VM range structures\n");
nuclear@22 474 }
nuclear@23 475 node_pool = (struct page_range*)PAGE_TO_ADDR(pg);
nuclear@22 476
nuclear@23 477 /* link them up, skip the first as we'll just allocate it anyway */
nuclear@23 478 for(i=2; i<NODES_IN_PAGE; i++) {
nuclear@23 479 node_pool[i - 1].next = node_pool + i;
nuclear@23 480 }
nuclear@23 481 node_pool[NODES_IN_PAGE - 1].next = 0;
nuclear@23 482
nuclear@23 483 /* grab the first and return it */
nuclear@23 484 node = node_pool++;
nuclear@47 485 /*printf("alloc_node -> %x\n", (unsigned int)node);*/
nuclear@23 486 return node;
nuclear@22 487 }
nuclear@22 488
nuclear@22 489 static void free_node(struct page_range *node)
nuclear@22 490 {
nuclear@22 491 node->next = node_pool;
nuclear@22 492 node_pool = node;
nuclear@47 493 /*printf("free_node\n");*/
nuclear@22 494 }
nuclear@23 495
nuclear@23 496
nuclear@47 497 /* clone_vm makes a copy of the current page tables, thus duplicating the
nuclear@47 498 * virtual address space.
nuclear@47 499 *
nuclear@47 500 * For the kernel part of the address space (last 256 page directory entries)
nuclear@47 501 * we don't want to diplicate the page tables, just point all page directory
nuclear@47 502 * entries to the same set of page tables.
nuclear@43 503 *
nuclear@43 504 * Returns the physical address of the new page directory.
nuclear@43 505 */
nuclear@47 506 uint32_t clone_vm(void)
nuclear@43 507 {
nuclear@48 508 int i, dirpg, tblpg, kstart_dirent;
nuclear@43 509 uint32_t paddr;
nuclear@43 510 uint32_t *ndir, *ntbl;
nuclear@43 511
nuclear@47 512 /* allocate the new page directory */
nuclear@43 513 if((dirpg = pgalloc(1, MEM_KERNEL)) == -1) {
nuclear@43 514 panic("clone_vmem: failed to allocate page directory page\n");
nuclear@43 515 }
nuclear@43 516 ndir = (uint32_t*)PAGE_TO_ADDR(dirpg);
nuclear@43 517
nuclear@47 518 /* allocate a virtual page for temporarily mapping all new
nuclear@47 519 * page tables while we populate them.
nuclear@47 520 */
nuclear@43 521 if((tblpg = pgalloc(1, MEM_KERNEL)) == -1) {
nuclear@43 522 panic("clone_vmem: failed to allocate page table page\n");
nuclear@43 523 }
nuclear@43 524 ntbl = (uint32_t*)PAGE_TO_ADDR(tblpg);
nuclear@43 525
nuclear@43 526 /* we will allocate physical pages and map them to this virtual page
nuclear@43 527 * as needed in the loop below.
nuclear@43 528 */
nuclear@43 529 free_phys_page(virt_to_phys(tblpg));
nuclear@43 530
nuclear@48 531 kstart_dirent = ADDR_TO_PAGE(KMEM_START) / 1024;
nuclear@47 532
nuclear@47 533 /* user space */
nuclear@48 534 for(i=0; i<kstart_dirent; i++) {
nuclear@43 535 if(pgdir[i] & PG_PRESENT) {
nuclear@43 536 paddr = alloc_phys_page();
nuclear@43 537 map_page(tblpg, ADDR_TO_PAGE(paddr), 0);
nuclear@43 538
nuclear@43 539 /* copy the page table */
nuclear@43 540 memcpy(ntbl, PGTBL(i), PGSIZE);
nuclear@43 541
nuclear@43 542 /* set the new page directory entry */
nuclear@43 543 ndir[i] = paddr | (pgdir[i] & PGOFFS_MASK);
nuclear@43 544 } else {
nuclear@43 545 ndir[i] = 0;
nuclear@43 546 }
nuclear@43 547 }
nuclear@43 548
nuclear@47 549 /* kernel space */
nuclear@48 550 for(i=kstart_dirent; i<1024; i++) {
nuclear@47 551 ndir[i] = *PGTBL(i);
nuclear@47 552 }
nuclear@47 553
nuclear@43 554 paddr = virt_to_phys(dirpg);
nuclear@43 555
nuclear@43 556 /* unmap before freeing to avoid deallocating the physical pages */
nuclear@43 557 unmap_page(dirpg);
nuclear@43 558 unmap_page(tblpg);
nuclear@43 559
nuclear@43 560 pgfree(dirpg, 1);
nuclear@43 561 pgfree(tblpg, 1);
nuclear@43 562
nuclear@43 563 return paddr;
nuclear@43 564 }
nuclear@43 565
nuclear@43 566
nuclear@23 567 void dbg_print_vm(int area)
nuclear@23 568 {
nuclear@25 569 struct page_range *node;
nuclear@25 570 int last, intr_state;
nuclear@25 571
nuclear@25 572 intr_state = get_intr_state();
nuclear@25 573 disable_intr();
nuclear@25 574
nuclear@25 575 node = pglist[area];
nuclear@25 576 last = area == MEM_USER ? 0 : ADDR_TO_PAGE(KMEM_START);
nuclear@23 577
nuclear@23 578 printf("%s vm space\n", area == MEM_USER ? "user" : "kernel");
nuclear@23 579
nuclear@23 580 while(node) {
nuclear@23 581 if(node->start > last) {
nuclear@23 582 printf(" vm-used: %x -> %x\n", PAGE_TO_ADDR(last), PAGE_TO_ADDR(node->start));
nuclear@23 583 }
nuclear@23 584
nuclear@23 585 printf(" vm-free: %x -> ", PAGE_TO_ADDR(node->start));
nuclear@23 586 if(node->end >= PAGE_COUNT) {
nuclear@23 587 printf("END\n");
nuclear@23 588 } else {
nuclear@23 589 printf("%x\n", PAGE_TO_ADDR(node->end));
nuclear@23 590 }
nuclear@23 591
nuclear@23 592 last = node->end;
nuclear@23 593 node = node->next;
nuclear@23 594 }
nuclear@25 595
nuclear@25 596 set_intr_state(intr_state);
nuclear@23 597 }