2 * Paging (virtual memory) support
3 * Copyright (c) 2003, Jeffrey K. Hollingsworth <hollings@cs.umd.edu>
4 * Copyright (c) 2003,2004 David H. Hovemeyer <daveho@cs.umd.edu>
7 * This is free software. You are permitted to use,
8 * redistribute, and modify it as specified in the file "COPYING".
11 #include <geekos/string.h>
12 #include <geekos/int.h>
13 #include <geekos/idt.h>
14 #include <geekos/kthread.h>
15 #include <geekos/kassert.h>
16 #include <geekos/screen.h>
17 #include <geekos/mem.h>
18 #include <geekos/malloc.h>
19 #include <geekos/gdt.h>
20 #include <geekos/segment.h>
21 //#include <geekos/user.h>
22 //#include <geekos/vfs.h>
23 #include <geekos/crc32.h>
24 #include <geekos/paging.h>
25 #include <geekos/serial.h>
28 /* ----------------------------------------------------------------------
30 * ---------------------------------------------------------------------- */
32 /* ----------------------------------------------------------------------
33 * Private functions/data
34 * ---------------------------------------------------------------------- */
36 #define SECTORS_PER_PAGE (PAGE_SIZE / SECTOR_SIZE)
39 * flag to indicate if debugging paging code
42 #define Debug(args...) if (debugFaults) Print(args)
46 void SerialPrintPD(pde_t *pde)
50 SerialPrint("Page Directory at %p:\n",pde);
51 for (i = 0; i < NUM_PAGE_DIR_ENTRIES; i++) {
53 if ((i * PAGE_SIZE * 1024) > 0x40000000) {
54 SerialPrintPDE((void*)(PAGE_SIZE*NUM_PAGE_TABLE_ENTRIES*i),&(pde[i]));
60 void SerialPrintPT(void *starting_address, pte_t *pte)
64 SerialPrint("Page Table at %p:\n",pte);
65 for (i=0;i<NUM_PAGE_TABLE_ENTRIES;i++) {
67 SerialPrintPTE(starting_address + PAGE_SIZE*i,&(pte[i]));
73 void SerialPrintPDE(void *virtual_address, pde_t *pde)
75 SerialPrint("PDE %p -> %p : present=%x, flags=%x, accessed=%x, reserved=%x, largePages=%x, globalPage=%x, kernelInfo=%x\n",
77 (void*) (pde->pageTableBaseAddr << PAGE_POWER),
87 void SerialPrintPTE(void *virtual_address, pte_t *pte)
89 SerialPrint("PTE %p -> %p : present=%x, flags=%x, accessed=%x, dirty=%x, pteAttribute=%x, globalPage=%x, kernelInfo=%x\n",
91 (void*)(pte->pageBaseAddr << PAGE_POWER),
102 void SerialDumpPageTables(pde_t *pde)
106 SerialPrint("Dumping the pages starting with the pde page at %p\n",pde);
108 for (i = 0; i < NUM_PAGE_DIR_ENTRIES; i++) {
109 if (pde[i].present) {
110 if ((i * PAGE_SIZE * 1024) >= 0x40000000) {
111 SerialPrintPDE((void *)(PAGE_SIZE * NUM_PAGE_TABLE_ENTRIES * i), &(pde[i]));
112 SerialPrintPT((void *)(PAGE_SIZE * NUM_PAGE_TABLE_ENTRIES * i), (void *)(pde[i].pageTableBaseAddr << PAGE_POWER));
125 __asm__ __volatile__( "movl %%cr0, %0" : "=a" (reg));
126 Print("Paging on ? : %d\n", (reg & (1<<31)) != 0);
127 return (reg & (1<<31)) != 0;
132 * Print diagnostic information for a page fault.
134 static void Print_Fault_Info(uint_t address, faultcode_t faultCode)
136 extern uint_t g_freePageCount;
140 SerialPrintLevel(100,"Pid %d, Page Fault received, at address %x (%d pages free)\n",
141 g_currentThread->pid, address, g_freePageCount);
142 if (faultCode.protectionViolation)
143 SerialPrintLevel(100," Protection Violation, ");
145 SerialPrintLevel(100," Non-present page, ");
146 if (faultCode.writeFault)
147 SerialPrintLevel(100,"Write Fault, ");
149 SerialPrintLevel(100,"Read Fault, ");
150 if (faultCode.userModeFault)
151 SerialPrintLevel(100,"in User Mode\n");
153 SerialPrintLevel(100,"in Supervisor Mode\n");
157 * Handler for page faults.
158 * You should call the Install_Interrupt_Handler() function to
159 * register this function as the handler for interrupt 14.
161 /*static*/ void Page_Fault_Handler(struct Interrupt_State* state)
164 faultcode_t faultCode;
166 KASSERT(!Interrupts_Enabled());
168 /* Get the address that caused the page fault */
169 address = Get_Page_Fault_Address();
170 Debug("Page fault @%lx\n", address);
172 /* Get the fault code */
173 faultCode = *((faultcode_t *) &(state->errorCode));
175 /* rest of your handling code here */
176 SerialPrintLevel(100,"Unexpected Page Fault received\n");
177 Print_Fault_Info(address, faultCode);
178 Dump_Interrupt_State(state);
179 /* user faults just kill the process */
180 if (!faultCode.userModeFault) KASSERT(0);
182 /* For now, just kill the thread/process. */
186 /* ----------------------------------------------------------------------
188 * ---------------------------------------------------------------------- */
193 * Initialize virtual memory by building page tables
194 * for the kernel and physical memory.
196 void Init_VM(struct Boot_Info *bootInfo)
205 PrintBoth("Intitialing Virtual Memory\n");
208 SerialPrintLevel(100,"Paging is currently ON\n");
212 SerialPrintLevel(100,"Paging is currently OFF - initializing the pages for a 1-1 map\n");
214 numpages=bootInfo->memSizeKB / (PAGE_SIZE/1024);
215 numpagetables = numpages / NUM_PAGE_TABLE_ENTRIES + ((numpages % NUM_PAGE_TABLE_ENTRIES) != 0 );
217 SerialPrintLevel(100,"We need %d pages, and thus %d page tables, and one page directory\n",numpages, numpagetables);
219 pd = (pde_t*)Alloc_Page();
222 SerialPrintLevel(100,"We are giving up since we can't allocate a page directory!\n");
225 SerialPrintLevel(100,"Our PDE is at physical address %p\n",pd);
228 for (i=0;i<NUM_PAGE_DIR_ENTRIES;i++) {
229 if (i>=numpagetables) {
237 pd[i].pageTableBaseAddr=0;
239 pt = (pte_t*)Alloc_Page();
241 SerialPrintLevel(100,"We are giving up since we can't allocate page table %d\n",i);
243 //SerialPrintLevel(100,"Page Table %d is at physical address %p\n",i,pt);
246 pd[i].flags= VM_READ | VM_WRITE | VM_EXEC | VM_USER;
252 pd[i].pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pt);
254 for (j=0;j<NUM_PAGE_TABLE_ENTRIES;j++) {
255 if (i*NUM_PAGE_TABLE_ENTRIES + j >= numpages) {
260 pt[j].pteAttribute=0;
263 pt[j].pageBaseAddr=0;
266 pt[j].flags=VM_READ | VM_WRITE | VM_EXEC | VM_USER;
269 pt[j].pteAttribute=0;
272 pt[j].pageBaseAddr=(i*NUM_PAGE_TABLE_ENTRIES + j);
279 SerialPrintLevel(100,"Done creating 1<->1 initial page tables\n");
280 SerialPrintLevel(100,"Now installing page fault handler\n");
281 // SerialDumpPageTables(pd);
282 Install_Interrupt_Handler(14,Page_Fault_Handler);
283 SerialPrintLevel(100,"Now turning on the paging bit!\n");
285 SerialPrintLevel(100,"We are still alive after paging turned on!\n");
286 SerialPrintLevel(100,"checkPaging returns %d\n",checkPaging());
292 * 1 <-> 1 mapping of physical memory,
293 * We assume that physical memory << 1G
294 * we setup the test so that 1G+ is mapped in a testable order
295 * then go through and ensure that all the paging operations work...
296 * The static mapping sits at 1G
297 * The tests are run at 2G
301 void VM_Test(struct Boot_Info *bootInfo, uint_t num_test_pages) {
308 PrintBoth("Running Paging Test\n");
313 void * one_gig = (void *)0x40000000;
314 void * two_gig = (void *)0x80000000;
316 /* Set up the 1 GIG static map */
317 ulong_t pde_offset = (((ulong_t)one_gig / PAGE_SIZE) / 1024);
318 for (i = 0; i < num_test_pages; i += 1024) {
319 pde_t * pde = &(pd[pde_offset + (i / 1024)]);
320 pte_t * pte = (pte_t *)Alloc_Page();
321 memset(pte, 0, PAGE_SIZE);
324 pde->flags= VM_READ | VM_WRITE | VM_EXEC | VM_USER;
325 pde->pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pte);
327 for (j = 0; (j + i) < num_test_pages ; j++) {
329 pte[j].flags = VM_READ | VM_WRITE | VM_EXEC | VM_USER;
330 pte[j].pageBaseAddr = PAGE_ALLIGNED_ADDR(Alloc_Page());
334 PrintBoth("Setup VM Test static map\n");
337 /* Setup the Two Gig test area */
338 /* First is just a incrmental mirroring of the 1G area */
340 pde_offset = (((ulong_t)two_gig / PAGE_SIZE) / 1024);
342 for (i = 0; i < num_test_pages; i += 1024) {
343 pde_t * pde = &(pd[pde_offset + (i / 1024)]);
344 pte_t * pte = (pte_t *)Alloc_Page();
345 memset(pte, 0, PAGE_SIZE);
348 pde->flags= VM_READ | VM_WRITE | VM_EXEC | VM_USER;
349 pde->pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pte);
351 for (j = 0; (j + i) < num_test_pages; j++) {
352 pte_t * static_pte = LookupPage(one_gig + (PAGE_SIZE * (j+i)));
354 pte[j].flags = VM_READ | VM_WRITE | VM_EXEC | VM_USER;
355 pte[j].pageBaseAddr = static_pte->pageBaseAddr;
359 PrintBoth("Setup initial test area\n");
361 PrintBoth("Loading CR3\n");
364 SerialDumpPageTables(pd);
366 PrintBoth("Writing to Test Area\n");
368 /* Write data to each page in the 2G test area */
369 uint_t * test_ptr = (uint_t *)two_gig;
370 for (i = 0; i < num_test_pages; i++) {
372 SerialPrint("Writing %d to %p\n", i, test_ptr);
373 *test_ptr = (uint_t)i;
374 test_ptr += PAGE_SIZE / 4;
378 PrintBoth("Reversing Page Mapping\n");
380 /* Reverse 2G test area mapping */
382 pde_offset = (((ulong_t)two_gig / PAGE_SIZE) / 1024);
384 for (i = 0; i < num_test_pages; i += 1024) {
385 pde_t * pde = &(pd[pde_offset + (i / 1024)]);
386 pte_t * pte = (pte_t *)(pde->pageTableBaseAddr << 12);
388 for (j = 0; (j + i) < num_test_pages ; j++) {
389 pte_t * static_pte = LookupPage(one_gig + (PAGE_SIZE * (num_test_pages - (j+i) - 1)));
390 pte[j].pageBaseAddr = static_pte->pageBaseAddr;
397 PrintBoth("Page Mapping Reversed\n");
398 SerialDumpPageTables(pd);
401 PrintBoth("Page Consistency Check\n");
403 test_ptr = (uint_t *)two_gig;
404 for (i = 0; i < num_test_pages; i++) {
405 if ((*test_ptr) != num_test_pages - (i+1)) {
406 PrintBoth("Consistency Error: (Test Value=%d; Actual Value=%d)\n", (num_test_pages - i), (*test_ptr));
409 test_ptr += PAGE_SIZE / 4;
412 PrintBoth("Test Sucessful\n");
415 PrintBoth("Invalidation Test\n");
418 Invalidate_PG(two_gig);
422 foo = *(uint_t *)two_gig;
423 *(uint_t *)((char *)two_gig + 4) = foo;
426 PrintBoth("Invalidation Test Successful\n");
431 pte_t *LookupPage(void *vaddr)
433 uint_t pde_offset = ((uint_t)vaddr) >> 22;
434 uint_t pte_offset = (((uint_t)vaddr) >> 12) & 0x3ff;
436 pde_t *pde = Get_PDBR();
442 if (!(pde->present)) {
446 pte = (pte_t *)((pde->pageTableBaseAddr)<<12);
454 pte_t *CreateAndAddPageTable(void *vaddr, uint_t flags)
458 KASSERT(!(PAGE_OFFSET(vaddr)));
460 pte_t *pt = Alloc_Page();
464 for (i=0;i<NUM_PAGE_TABLE_ENTRIES;i++) {
468 pde_t *pde = Get_PDBR();
470 pde=&(pde[PAGE_DIRECTORY_INDEX(vaddr)]);
472 KASSERT(!(pde->present));
481 pde->pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pt);
486 pte_t *MapPage(void *vaddr, pte_t *pte, int alloc_pde)
488 pte_t *oldpte = LookupPage(vaddr);
492 CreateAndAddPageTable(vaddr,pte->flags);
493 oldpte = LookupPage(vaddr);
505 pte_t *UnMapPage(void *vaddr)
507 pte_t *oldpte = LookupPage(vaddr);
520 * Initialize paging file data structures.
521 * All filesystems should be mounted before this function
522 * is called, to ensure that the paging file is available.
524 void Init_Paging(void)
526 PrintBoth("Initializing Paging\n");
530 * Find a free bit of disk on the paging file for this page.
531 * Interrupts must be disabled.
532 * @return index of free page sized chunk of disk space in
533 * the paging file, or -1 if the paging file is full
535 int Find_Space_On_Paging_File(void)
537 KASSERT(!Interrupts_Enabled());
538 TODO("Find free page in paging file");
542 * Free a page-sized chunk of disk space in the paging file.
543 * Interrupts must be disabled.
544 * @param pagefileIndex index of the chunk of disk space
546 void Free_Space_On_Paging_File(int pagefileIndex)
548 KASSERT(!Interrupts_Enabled());
549 TODO("Free page in paging file");
553 * Write the contents of given page to the indicated block
554 * of space in the paging file.
555 * @param paddr a pointer to the physical memory of the page
556 * @param vaddr virtual address where page is mapped in user memory
557 * @param pagefileIndex the index of the page sized chunk of space
560 void Write_To_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex)
562 struct Page *page = Get_Page((ulong_t) paddr);
563 KASSERT(!(page->flags & PAGE_PAGEABLE)); /* Page must be locked! */
564 TODO("Write page data to paging file");
568 * Read the contents of the indicated block
569 * of space in the paging file into the given page.
570 * @param paddr a pointer to the physical memory of the page
571 * @param vaddr virtual address where page will be re-mapped in
573 * @param pagefileIndex the index of the page sized chunk of space
576 void Read_From_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex)
578 struct Page *page = Get_Page((ulong_t) paddr);
579 KASSERT(!(page->flags & PAGE_PAGEABLE)); /* Page must be locked! */
580 TODO("Read page data from paging file");