Palacios Public Git Repository

To checkout Palacios execute

  git clone http://v3vee.org/palacios/palacios.web/palacios.git
This will give you the master branch. You probably want the devel branch or one of the release branches. To switch to the devel branch, simply execute
  cd palacios
  git checkout --track -b devel origin/devel
The other branches are similar.


Release 1.0
[palacios.git] / misc / test_vm / src / geekos / paging.c
1 /*
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>
5  * $Revision: 1.2 $
6  * 
7  * This is free software.  You are permitted to use,
8  * redistribute, and modify it as specified in the file "COPYING".
9  */
10
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>
26
27
28 /* ----------------------------------------------------------------------
29  * Public data
30  * ---------------------------------------------------------------------- */
31
32 /* ----------------------------------------------------------------------
33  * Private functions/data
34  * ---------------------------------------------------------------------- */
35
36 #define SECTORS_PER_PAGE (PAGE_SIZE / SECTOR_SIZE)
37
38 /*
39  * flag to indicate if debugging paging code
40  */
41 int debugFaults = 0;
42 #define Debug(args...) if (debugFaults) Print(args)
43
44
45
46 void SerialPrintPD(pde_t *pde)
47 {
48   uint_t i;
49
50   SerialPrint("Page Directory at %p:\n",pde);
51   for (i = 0; i < NUM_PAGE_DIR_ENTRIES; i++) { 
52     if (pde[i].present) {
53       if ((i * PAGE_SIZE * 1024) > 0x40000000) {
54         SerialPrintPDE((void*)(PAGE_SIZE*NUM_PAGE_TABLE_ENTRIES*i),&(pde[i]));
55       }
56     }
57   }
58 }
59
60 void SerialPrintPT(void *starting_address, pte_t *pte) 
61 {
62   int i;
63
64   SerialPrint("Page Table at %p:\n",pte);
65   for (i=0;i<NUM_PAGE_TABLE_ENTRIES;i++) { 
66     if (pte[i].present) {
67       SerialPrintPTE(starting_address + PAGE_SIZE*i,&(pte[i]));
68     }
69   }
70 }
71
72
73 void SerialPrintPDE(void *virtual_address, pde_t *pde)
74 {
75   SerialPrint("PDE %p -> %p : present=%x, flags=%x, accessed=%x, reserved=%x, largePages=%x, globalPage=%x, kernelInfo=%x\n",
76               virtual_address,
77               (void*) (pde->pageTableBaseAddr << PAGE_POWER),
78               pde->present,
79               pde->flags,
80               pde->accessed,
81               pde->reserved,
82               pde->largePages,
83               pde->globalPage,
84               pde->kernelInfo);
85 }
86   
87 void SerialPrintPTE(void *virtual_address, pte_t *pte)
88 {
89   SerialPrint("PTE %p -> %p : present=%x, flags=%x, accessed=%x, dirty=%x, pteAttribute=%x, globalPage=%x, kernelInfo=%x\n",
90               virtual_address,
91               (void*)(pte->pageBaseAddr << PAGE_POWER),
92               pte->present,
93               pte->flags,
94               pte->accessed,
95               pte->dirty,
96               pte->pteAttribute,
97               pte->globalPage,
98               pte->kernelInfo);
99 }
100
101
102 void SerialDumpPageTables(pde_t *pde)
103 {
104   uint_t i;
105   
106   SerialPrint("Dumping the pages starting with the pde page at %p\n",pde);
107
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));
113       }
114     }
115   }
116 }
117     
118     
119     
120
121
122 int checkPaging()
123 {
124   unsigned long reg=0;
125   __asm__ __volatile__( "movl %%cr0, %0" : "=a" (reg));
126   Print("Paging on ? : %d\n", (reg & (1<<31)) != 0);
127   return (reg & (1<<31)) != 0;
128 }
129
130
131 /*
132  * Print diagnostic information for a page fault.
133  */
134 static void Print_Fault_Info(uint_t address, faultcode_t faultCode)
135 {
136     extern uint_t g_freePageCount;
137
138     g_freePageCount+=0;
139
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, ");
144     else
145         SerialPrintLevel(100,"   Non-present page, ");
146     if (faultCode.writeFault)
147         SerialPrintLevel(100,"Write Fault, ");
148     else
149         SerialPrintLevel(100,"Read Fault, ");
150     if (faultCode.userModeFault)
151         SerialPrintLevel(100,"in User Mode\n");
152     else
153         SerialPrintLevel(100,"in Supervisor Mode\n");
154 }
155
156 /*
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.
160  */
161 /*static*/ void Page_Fault_Handler(struct Interrupt_State* state)
162 {
163     ulong_t address;
164     faultcode_t faultCode;
165
166     KASSERT(!Interrupts_Enabled());
167
168     /* Get the address that caused the page fault */
169     address = Get_Page_Fault_Address();
170     Debug("Page fault @%lx\n", address);
171
172     /* Get the fault code */
173     faultCode = *((faultcode_t *) &(state->errorCode));
174
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);
181
182     /* For now, just kill the thread/process. */
183     Exit(-1);
184 }
185
186 /* ----------------------------------------------------------------------
187  * Public functions
188  * ---------------------------------------------------------------------- */
189
190
191
192 /*
193  * Initialize virtual memory by building page tables
194  * for the kernel and physical memory.
195  */
196 void Init_VM(struct Boot_Info *bootInfo)
197 {
198   int numpages;
199   int numpagetables;
200   int i,j;
201
202   pde_t *pd;
203   pte_t *pt;
204
205   PrintBoth("Intitialing Virtual Memory\n");
206
207   if (checkPaging()) { 
208     SerialPrintLevel(100,"Paging is currently ON\n");
209     return ;
210   }
211
212   SerialPrintLevel(100,"Paging is currently OFF - initializing the pages for a 1-1 map\n");
213   
214   numpages=bootInfo->memSizeKB / (PAGE_SIZE/1024);
215   numpagetables = numpages / NUM_PAGE_TABLE_ENTRIES + ((numpages % NUM_PAGE_TABLE_ENTRIES) != 0 );
216
217   SerialPrintLevel(100,"We need %d pages, and thus %d page tables, and one page directory\n",numpages, numpagetables);
218   
219   pd = (pde_t*)Alloc_Page();
220   
221   if (!pd) { 
222     SerialPrintLevel(100,"We are giving up since we can't allocate a page directory!\n");
223     return;
224   } else {
225     SerialPrintLevel(100,"Our PDE is at physical address %p\n",pd);
226   }
227   
228   for (i=0;i<NUM_PAGE_DIR_ENTRIES;i++) { 
229     if (i>=numpagetables) { 
230       pd[i].present=0;
231       pd[i].flags=0;
232       pd[i].accessed=0;
233       pd[i].reserved=0;
234       pd[i].largePages=0;
235       pd[i].globalPage=0;
236       pd[i].kernelInfo=0;
237       pd[i].pageTableBaseAddr=0;
238     } else {
239       pt = (pte_t*)Alloc_Page();
240       if (!pt) { 
241         SerialPrintLevel(100,"We are giving up since we can't allocate page table %d\n",i);
242       } else {
243         //SerialPrintLevel(100,"Page Table %d is at physical address %p\n",i,pt);
244       }
245       pd[i].present=1;
246       pd[i].flags= VM_READ | VM_WRITE | VM_EXEC | VM_USER;
247       pd[i].accessed=0;
248       pd[i].reserved=0;
249       pd[i].largePages=0;
250       pd[i].globalPage=0;
251       pd[i].kernelInfo=0;
252       pd[i].pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pt);
253       
254       for (j=0;j<NUM_PAGE_TABLE_ENTRIES;j++) { 
255         if (i*NUM_PAGE_TABLE_ENTRIES + j >= numpages) {
256           pt[j].present=0;
257           pt[j].flags=0;
258           pt[j].accessed=0;
259           pt[j].dirty=0;
260           pt[j].pteAttribute=0;
261           pt[j].globalPage=0;
262           pt[j].kernelInfo=0;
263           pt[j].pageBaseAddr=0;
264         } else {
265           pt[j].present=1;
266           pt[j].flags=VM_READ | VM_WRITE | VM_EXEC | VM_USER;
267           pt[j].accessed=0;
268           pt[j].dirty=0;
269           pt[j].pteAttribute=0;
270           pt[j].globalPage=0;
271           pt[j].kernelInfo=0;
272           pt[j].pageBaseAddr=(i*NUM_PAGE_TABLE_ENTRIES + j);
273         }
274       }
275     }
276   }
277
278
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");
284   Enable_Paging(pd);
285   SerialPrintLevel(100,"We are still alive after paging turned on!\n");
286   SerialPrintLevel(100,"checkPaging returns %d\n",checkPaging());
287 }
288
289
290
291 /* How we test...
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
298
299  */
300
301 void VM_Test(struct Boot_Info *bootInfo, uint_t num_test_pages) {
302   int i,j;
303
304   pde_t * pd;
305   //pde_t *pde;
306   //  pte_t *pte;
307
308   PrintBoth("Running Paging Test\n");
309
310   pd = Get_PDBR();
311
312
313   void * one_gig = (void *)0x40000000;
314   void * two_gig = (void *)0x80000000;
315
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);
322
323     pde->present = 1;
324     pde->flags= VM_READ | VM_WRITE | VM_EXEC | VM_USER;
325     pde->pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pte);
326
327     for (j = 0; (j + i) < num_test_pages ; j++) {
328       pte[j].present = 1;
329       pte[j].flags = VM_READ | VM_WRITE | VM_EXEC | VM_USER;
330       pte[j].pageBaseAddr = PAGE_ALLIGNED_ADDR(Alloc_Page());
331     }
332   }
333
334   PrintBoth("Setup VM Test static map\n");
335
336
337   /* Setup the Two Gig test area */
338   /* First is just a incrmental mirroring of the 1G area */
339
340   pde_offset = (((ulong_t)two_gig / PAGE_SIZE) / 1024);  
341   
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);
346
347     pde->present = 1;
348     pde->flags= VM_READ | VM_WRITE | VM_EXEC | VM_USER;
349     pde->pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pte);
350
351     for (j = 0; (j + i) < num_test_pages; j++) {
352       pte_t * static_pte = LookupPage(one_gig + (PAGE_SIZE * (j+i)));
353       pte[j].present = 1;
354       pte[j].flags = VM_READ | VM_WRITE | VM_EXEC | VM_USER;
355       pte[j].pageBaseAddr = static_pte->pageBaseAddr;
356     }
357   }
358
359   PrintBoth("Setup initial test area\n");
360
361   PrintBoth("Loading CR3\n");
362   Set_PDBR(pd);
363
364   SerialDumpPageTables(pd);
365
366   PrintBoth("Writing to Test Area\n");
367
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++) {
371
372     SerialPrint("Writing %d to %p\n", i, test_ptr);
373     *test_ptr = (uint_t)i;
374     test_ptr += PAGE_SIZE / 4;
375   }
376
377
378   PrintBoth("Reversing Page Mapping\n");
379   
380   /* Reverse 2G test area mapping */
381   
382   pde_offset = (((ulong_t)two_gig / PAGE_SIZE) / 1024);
383
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);
387
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;
391     } 
392   }
393
394
395   Set_PDBR(pd);
396   
397   PrintBoth("Page Mapping Reversed\n");
398   SerialDumpPageTables(pd);
399
400
401   PrintBoth("Page Consistency Check\n");
402
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));
407       while(1);
408     }
409     test_ptr += PAGE_SIZE / 4;
410   }
411
412   PrintBoth("Test Sucessful\n");
413
414
415   PrintBoth("Invalidation Test\n");
416
417
418   Invalidate_PG(two_gig);
419
420   uint_t foo = 0;
421
422   foo = *(uint_t *)two_gig;
423   *(uint_t *)((char *)two_gig + 4) = foo;
424
425
426   PrintBoth("Invalidation Test Successful\n");
427 }
428
429
430 //
431 pte_t *LookupPage(void *vaddr)
432 {
433   uint_t pde_offset = ((uint_t)vaddr) >> 22;
434   uint_t pte_offset = (((uint_t)vaddr) >> 12) & 0x3ff;
435   pte_t *pte; 
436   pde_t *pde = Get_PDBR();
437
438   KASSERT(pde);
439
440   pde+=pde_offset;
441
442   if (!(pde->present)) { 
443     return 0;
444   }
445
446   pte = (pte_t *)((pde->pageTableBaseAddr)<<12);
447
448   pte+=pte_offset;
449
450   return pte;
451 }
452
453
454 pte_t *CreateAndAddPageTable(void *vaddr, uint_t flags)
455 {
456   int i;
457
458   KASSERT(!(PAGE_OFFSET(vaddr)));
459   
460   pte_t *pt = Alloc_Page();
461   
462   KASSERT(pt);
463   
464   for (i=0;i<NUM_PAGE_TABLE_ENTRIES;i++) { 
465     pt[i].present=0;
466   }
467
468   pde_t *pde = Get_PDBR();
469   
470   pde=&(pde[PAGE_DIRECTORY_INDEX(vaddr)]);
471
472   KASSERT(!(pde->present));
473   
474   pde->present=1;
475   pde->flags=flags;
476   pde->accessed=0;
477   pde->reserved=0;
478   pde->largePages=0;
479   pde->globalPage=0;
480   pde->kernelInfo=0;
481   pde->pageTableBaseAddr = PAGE_ALLIGNED_ADDR(pt);
482
483   return pt;
484 }
485
486 pte_t *MapPage(void *vaddr, pte_t *pte, int alloc_pde)
487 {
488   pte_t *oldpte = LookupPage(vaddr);
489
490   if (!pte) {
491     if (alloc_pde) { 
492       CreateAndAddPageTable(vaddr,pte->flags);
493       oldpte = LookupPage(vaddr);
494       KASSERT(pte);
495     } else {
496       return 0;
497     }
498   }
499
500   *oldpte = *pte;
501   
502   return oldpte;
503 }
504
505 pte_t *UnMapPage(void *vaddr)
506 {
507   pte_t *oldpte = LookupPage(vaddr);
508
509   if (!oldpte) {
510     return 0;
511   }
512   oldpte->present=0;
513   
514   return oldpte;
515 }
516
517   
518
519 /**
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.
523  */
524 void Init_Paging(void)
525 {
526   PrintBoth("Initializing Paging\n");
527 }
528
529 /**
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
534  */
535 int Find_Space_On_Paging_File(void)
536 {
537     KASSERT(!Interrupts_Enabled());
538     TODO("Find free page in paging file");
539 }
540
541 /**
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
545  */
546 void Free_Space_On_Paging_File(int pagefileIndex)
547 {
548     KASSERT(!Interrupts_Enabled());
549     TODO("Free page in paging file");
550 }
551
552 /**
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
558  *   in the paging file
559  */
560 void Write_To_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex)
561 {
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");
565 }
566
567 /**
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
572  *   user memory
573  * @param pagefileIndex the index of the page sized chunk of space
574  *   in the paging file
575  */
576 void Read_From_Paging_File(void *paddr, ulong_t vaddr, int pagefileIndex)
577 {
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");
581 }
582