kern

annotate src/vm.c @ 52:fa65b4f45366

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