Cleaning up and adding page fault handler
[tpg/acess2.git] / Kernel / arch / x86_64 / mm_virt.c
1 /*
2  * Acess2 x86_64 Port
3  * 
4  * Virtual Memory Manager
5  */
6 #include <acess.h>
7 #include <mm_virt.h>
8 #include <proc.h>
9
10 // === CONSTANTS ===
11 #define PML4_SHIFT      39
12 #define PDP_SHIFT       30
13 #define PDIR_SHIFT      21
14 #define PTAB_SHIFT      12
15
16 #define PADDR_MASK      0x7FFFFFFF##FFFFF000
17 #define PAGE_MASK       (((Uint)1 << 36)-1)
18 #define TABLE_MASK      (((Uint)1 << 27)-1)
19 #define PDP_MASK        (((Uint)1 << 18)-1)
20 #define PML4_MASK       (((Uint)1 << 9)-1)
21
22 #define PF_PRESENT      0x1
23 #define PF_WRITE        0x2
24 #define PF_USER         0x4
25 #define PF_COW          0x200
26 #define PF_PAGED        0x400
27 #define PF_NX           0x80000000##00000000
28
29 // === MACROS ===
30 #define PAGETABLE(idx)  (*((tPAddr*)MM_FRACTAL_BASE+((idx)&PAGE_MASK)))
31 #define PAGEDIR(idx)    PAGETABLE((MM_FRACTAL_BASE>>12)+((idx)&TABLE_MASK))
32 #define PAGEDIRPTR(idx) PAGEDIR((MM_FRACTAL_BASE>>21)+((idx)&PDP_MASK))
33 #define PAGEMAPLVL4(idx)        PAGEDIRPTR((MM_FRACTAL_BASE>>30)+((idx)&PML4_MASK))
34
35 // === PROTOTYPES ===
36 void    MM_InitVirt(void);
37 void    MM_FinishVirtualInit(void);
38 void    MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
39 void    MM_DumpTables(tVAddr Start, tVAddr End);
40  int    MM_Map(tVAddr VAddr, tPAddr PAddr);
41
42 // === GLOBALS ===
43
44 // === CODE ===
45 void MM_InitVirt(void)
46 {
47 }
48
49 void MM_FinishVirtualInit(void)
50 {
51 }
52
53 /**
54  * \brief Called on a page fault
55  */
56 void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
57 {
58         // TODO: Copy on Write
59         #if 0
60         if( gaPageDir  [Addr>>22] & PF_PRESENT
61          && gaPageTable[Addr>>12] & PF_PRESENT
62          && gaPageTable[Addr>>12] & PF_COW )
63         {
64                 tPAddr  paddr;
65                 if(MM_GetRefCount( gaPageTable[Addr>>12] & ~0xFFF ) == 1)
66                 {
67                         gaPageTable[Addr>>12] &= ~PF_COW;
68                         gaPageTable[Addr>>12] |= PF_PRESENT|PF_WRITE;
69                 }
70                 else
71                 {
72                         //Log("MM_PageFault: COW - MM_DuplicatePage(0x%x)", Addr);
73                         paddr = MM_DuplicatePage( Addr );
74                         MM_DerefPhys( gaPageTable[Addr>>12] & ~0xFFF );
75                         gaPageTable[Addr>>12] &= PF_USER;
76                         gaPageTable[Addr>>12] |= paddr|PF_PRESENT|PF_WRITE;
77                 }
78                 
79                 INVLPG( Addr & ~0xFFF );
80                 return;
81         }
82         #endif
83         
84         // If it was a user, tell the thread handler
85         if(ErrorCode & 4) {
86                 Warning("%s %s %s memory%s",
87                         (ErrorCode&4?"User":"Kernel"),
88                         (ErrorCode&2?"write to":"read from"),
89                         (ErrorCode&1?"bad/locked":"non-present"),
90                         (ErrorCode&16?" (Instruction Fetch)":"")
91                         );
92                 Warning("User Pagefault: Instruction at %04x:%08x accessed %p",
93                         Regs->CS, Regs->RIP, Addr);
94                 __asm__ __volatile__ ("sti");   // Restart IRQs
95 //              Threads_SegFault(Addr);
96                 return ;
97         }
98         
99         // Kernel #PF
100         Debug_KernelPanic();
101         // -- Check Error Code --
102         if(ErrorCode & 8)
103                 Warning("Reserved Bits Trashed!");
104         else
105         {
106                 Warning("%s %s %s memory%s",
107                         (ErrorCode&4?"User":"Kernel"),
108                         (ErrorCode&2?"write to":"read from"),
109                         (ErrorCode&1?"bad/locked":"non-present"),
110                         (ErrorCode&16?" (Instruction Fetch)":"")
111                         );
112         }
113         
114         Log("Code at %p accessed %p", Regs->RIP, Addr);
115         // Print Stack Backtrace
116 //      Error_Backtrace(Regs->RIP, Regs->RBP);
117         
118         MM_DumpTables(0, -1);
119 }
120
121 /**
122  * \brief Dumps the layout of the page tables
123  */
124 void MM_DumpTables(tVAddr Start, tVAddr End)
125 {
126         tVAddr  rangeStart = 0;
127         tPAddr  expected = 0;
128         tVAddr  curPos;
129         Uint    page;
130         const tPAddr    MASK = ~0xF98;  // Physical address and access bits
131         
132         Start >>= 12;   End >>= 12;
133         
134         Log("Table Entries:");
135         for(page = Start, curPos = Start<<12;
136                 page < (End & (((tVAddr)1 << 48)-1));
137                 curPos += 0x1000, page++)
138         {
139                 // End of a range
140                 if(
141                         !(PAGEMAPLVL4(page>>27) & PF_PRESENT)
142                 ||      !(PAGEDIRPTR(page>>18) & PF_PRESENT)
143                 ||      !(PAGEDIR(page>>9) & PF_PRESENT)
144                 ||  !(PAGETABLE(page) & PF_PRESENT)
145                 ||  (PAGETABLE(page) & MASK) != expected)
146                 {
147                         if(expected) {
148                                 Log(" 0x%08x-0x%08x => 0x%08x-0x%08x (%s%s%s%s)",
149                                         rangeStart, curPos - 1,
150                                         PAGETABLE(rangeStart>>12) & ~0xFFF,
151                                         (expected & ~0xFFF) - 1,
152                                         (expected & PF_PAGED ? "p" : "-"),
153                                         (expected & PF_COW ? "C" : "-"),
154                                         (expected & PF_USER ? "U" : "-"),
155                                         (expected & PF_WRITE ? "W" : "-")
156                                         );
157                                 expected = 0;
158                         }
159                         if( !(PAGEMAPLVL4(page>>27) & PF_PRESENT) )     continue;
160                         if( !(PAGEDIRPTR(page>>18) & PF_PRESENT) )      continue;
161                         if( !(PAGEDIR(page>>9) & PF_PRESENT) )  continue;
162                         if( !(PAGETABLE(page) & PF_PRESENT) )   continue;
163                         
164                         expected = (PAGETABLE(page) & MASK);
165                         rangeStart = curPos;
166                 }
167                 if(expected)    expected += 0x1000;
168         }
169         
170         if(expected) {
171                 Log("0x%08x-0x%08x => 0x%08x-0x%08x (%s%s%s%s)",
172                         rangeStart, curPos - 1,
173                         PAGETABLE(rangeStart>>12) & ~0xFFF,
174                         (expected & ~0xFFF) - 1,
175                         (expected & PF_PAGED ? "p" : "-"),
176                         (expected & PF_COW ? "C" : "-"),
177                         (expected & PF_USER ? "U" : "-"),
178                         (expected & PF_WRITE ? "W" : "-")
179                         );
180                 expected = 0;
181         }
182 }
183
184 /**
185  * \brief Map a physical page to a virtual one
186  */
187 int MM_Map(tVAddr VAddr, tPAddr PAddr)
188 {
189         tPAddr  tmp;
190         
191         Log("MM_Map: (VAddr=0x%x, PAddr=0x%x)", VAddr, PAddr);
192         
193         // Check PML4
194         //Log(" MM_Map: &PAGEMAPLVL4(%x) = %x", VAddr >> 39, &PAGEMAPLVL4(VAddr >> 39));
195         //Log(" MM_Map: &PAGEDIRPTR(%x) = %x", VAddr >> 30, &PAGEDIRPTR(VAddr >> 30));
196         //Log(" MM_Map: &PAGEDIR(%x) = %x", VAddr >> 21, &PAGEDIR(VAddr >> 21));
197         //Log(" MM_Map: &PAGETABLE(%x) = %x", VAddr >> 12, &PAGETABLE(VAddr >> 12));
198         //Log(" MM_Map: &PAGETABLE(0) = %x", &PAGETABLE(0));
199         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
200         {
201                 tmp = MM_AllocPhys();
202                 if(!tmp)        return 0;
203                 PAGEMAPLVL4(VAddr >> 39) = tmp | 3;
204                 memset( &PAGEDIRPTR( (VAddr>>39)<<9 ), 0, 4096 );
205         }
206         
207         // Check PDP
208         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
209         {
210                 tmp = MM_AllocPhys();
211                 if(!tmp)        return 0;
212                 PAGEDIRPTR(VAddr >> 30) = tmp | 3;
213                 memset( &PAGEDIR( (VAddr>>30)<<9 ), 0, 4096 );
214         }
215         
216         // Check Page Dir
217         if( !(PAGEDIR(VAddr >> 21) & 1) )
218         {
219                 tmp = MM_AllocPhys();
220                 if(!tmp)        return 0;
221                 PAGEDIR(VAddr >> 21) = tmp | 3;
222                 memset( &PAGETABLE( (VAddr>>21)<<9 ), 0, 4096 );
223         }
224         
225         // Check if this virtual address is already mapped
226         if( PAGETABLE(VAddr >> PTAB_SHIFT) & 1 )
227                 return 0;
228         
229         PAGETABLE(VAddr >> PTAB_SHIFT) = PAddr | 3;
230         
231         Log("MM_Map: RETURN 1");
232         
233         return 1;
234 }
235
236 /**
237  * \brief Removed a mapped page
238  */
239 void MM_Unmap(tVAddr VAddr)
240 {
241         // Check PML4
242         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )   return ;
243         // Check PDP
244         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )    return ;
245         // Check Page Dir
246         if( !(PAGEDIR(VAddr >> 21) & 1) )       return ;
247         
248         PAGETABLE(VAddr >> PTAB_SHIFT) = 0;
249 }
250
251 /**
252  * \brief Allocate a block of memory at the specified virtual address
253  */
254 tPAddr MM_Allocate(tVAddr VAddr)
255 {
256         tPAddr  ret;
257         
258         Log("MM_Allocate: (VAddr=%x)", VAddr);
259         Log("MM_Allocate: MM_AllocPhys()");
260         ret = MM_AllocPhys();
261         Log("MM_Allocate: ret = %x", ret);
262         if(!ret)        return 0;
263         
264         if( !MM_Map(VAddr, ret) )
265         {
266                 Warning("MM_Allocate: Unable to map", ret);
267                 MM_DerefPhys(ret);
268                 return 0;
269         }
270         
271         return ret;
272 }
273
274 void MM_Deallocate(tVAddr VAddr)
275 {
276         tPAddr  phys;
277         
278         phys = MM_GetPhysAddr(VAddr);
279         if(!phys)       return ;
280         
281         MM_Unmap(VAddr);
282         
283         MM_DerefPhys(phys);
284 }
285
286 /**
287  * \brief Get the physical address of a virtual location
288  */
289 tPAddr MM_GetPhysAddr(tVAddr Addr)
290 {
291         Log("MM_GetPhysAddr: (Addr=0x%x)", Addr);
292         if( !(PAGEMAPLVL4(Addr >> 39) & 1) )
293                 return 0;
294         Log(" MM_GetPhysAddr: PDP Valid");
295         if( !(PAGEDIRPTR(Addr >> 30) & 1) )
296                 return 0;
297         Log(" MM_GetPhysAddr: PD Valid");
298         if( !(PAGEDIR(Addr >> 21) & 1) )
299                 return 0;
300         Log(" MM_GetPhysAddr: PT Valid");
301         if( !(PAGETABLE(Addr >> PTAB_SHIFT) & 1) )
302                 return 0;
303         Log(" MM_GetPhysAddr: Page Valid");
304         
305         return (PAGETABLE(Addr >> PTAB_SHIFT) & ~0xFFF) | (Addr & 0xFFF);
306 }
307
308 /**
309  * \brief Sets the flags on a page
310  */
311 void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
312 {
313         tPAddr  *ent;
314         
315         // Validity Check
316         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
317                 return ;
318         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
319                 return ;
320         if( !(PAGEDIR(VAddr >> 21) & 1) )
321                 return ;
322         if( !(PAGETABLE(VAddr >> 12) & 1) )
323                 return ;
324         
325         // Set Flags
326         ent = &PAGETABLE(VAddr >> 12);
327         
328         // Read-Only
329         if( Mask & MM_PFLAG_RO )
330         {
331                 if( Flags & MM_PFLAG_RO ) {
332                         *ent &= ~PF_WRITE;
333                 }
334                 else {
335                         *ent |= PF_WRITE;
336                 }
337         }
338         
339         // Kernel
340         if( Mask & MM_PFLAG_KERNEL )
341         {
342                 if( Flags & MM_PFLAG_KERNEL ) {
343                         *ent &= ~PF_USER;
344                 }
345                 else {
346                         *ent |= PF_USER;
347                 }
348         }
349         
350         // Copy-On-Write
351         if( Mask & MM_PFLAG_COW )
352         {
353                 if( Flags & MM_PFLAG_COW ) {
354                         *ent &= ~PF_WRITE;
355                         *ent |= PF_COW;
356                 }
357                 else {
358                         *ent &= ~PF_COW;
359                         *ent |= PF_WRITE;
360                 }
361         }
362         
363         // Execute
364         if( Mask & MM_PFLAG_EXEC )
365         {
366                 if( Flags & MM_PFLAG_EXEC ) {
367                         *ent &= ~PF_NX;
368                 }
369                 else {
370                         *ent |= PF_NX;
371                 }
372         }
373 }
374
375 /**
376  * \brief Get the flags applied to a page
377  */
378 Uint MM_GetFlags(tVAddr VAddr)
379 {
380         tPAddr  *ent;
381         Uint    ret = 0;
382         
383         // Validity Check
384         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
385                 return 0;
386         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
387                 return 0;
388         if( !(PAGEDIR(VAddr >> 21) & 1) )
389                 return 0;
390         if( !(PAGETABLE(VAddr >> 12) & 1) )
391                 return 0;
392         
393         // Set Flags
394         ent = &PAGETABLE(VAddr >> 12);
395         
396         // Read-Only
397         if( !(*ent & PF_WRITE) )        ret |= MM_PFLAG_RO;
398         // Kernel
399         if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL;
400         // Copy-On-Write
401         if( *ent & PF_COW )     ret |= MM_PFLAG_COW;    
402         // Execute
403         if( !(*ent & PF_NX) )   ret |= MM_PFLAG_EXEC;
404         
405         return ret;
406 }
407
408 // --- Hardware Mappings ---
409 /**
410  * \brief Map a range of hardware pages
411  */
412 tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
413 {
414         Log_KernelPanic("MM", "TODO: Implement MM_MapHWPages");
415         return 0;
416 }
417
418 /**
419  * \brief Free a range of hardware pages
420  */
421 void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
422 {
423         Log_KernelPanic("MM", "TODO: Implement MM_UnmapHWPages");
424 }
425
426 // --- Tempory Mappings ---
427 tVAddr MM_MapTemp(tPAddr PAddr)
428 {
429         Log_KernelPanic("MM", "TODO: Implement MM_MapTemp");
430         return 0;
431 }
432
433 void MM_FreeTemp(tVAddr VAddr)
434 {
435         Log_KernelPanic("MM", "TODO: Implement MM_FreeTemp");
436         return ;
437 }
438
439
440 // --- Address Space Clone --
441 tPAddr MM_Clone(void)
442 {
443         tPAddr  ret;
444         
445         // #1 Create a copy of the PML4
446         ret = MM_AllocPhys();
447         if(!ret)        return 0;
448         
449         // #2 Alter the fractal pointer
450         // #3 Set Copy-On-Write to all user pages
451         // #4 Return
452         return 0;
453 }
454
455 void MM_ClearUser(void)
456 {
457         tVAddr  addr = 0;
458         // #1 Traverse the structure < 2^47, Deref'ing all pages
459         // #2 Free tables/dirs/pdps once they have been cleared
460         
461         for( addr = 0; addr < 0x800000000000; )
462         {
463                 if( PAGEMAPLVL4(addr >> PML4_SHIFT) & 1 )
464                 {
465                         if( PAGEDIRPTR(addr >> PDP_SHIFT) & 1 )
466                         {
467                                 if( PAGEDIR(addr >> PDIR_SHIFT) & 1 )
468                                 {
469                                         // Page
470                                         if( PAGETABLE(addr >> PTAB_SHIFT) & 1 ) {
471                                                 MM_DerefPhys( PAGETABLE(addr >> PTAB_SHIFT) & PADDR_MASK );
472                                                 PAGETABLE(addr >> PTAB_SHIFT) = 0;
473                                         }
474                                         addr += 1 << PTAB_SHIFT;
475                                         // Dereference the PDIR Entry
476                                         if( (addr + (1 << PTAB_SHIFT)) >> PDIR_SHIFT != (addr >> PDIR_SHIFT) ) {
477                                                 MM_DerefPhys( PAGEMAPLVL4(addr >> PDIR_SHIFT) & PADDR_MASK );
478                                                 PAGEDIR(addr >> PDIR_SHIFT) = 0;
479                                         }
480                                 }
481                                 else {
482                                         addr += 1 << PDIR_SHIFT;
483                                         continue;
484                                 }
485                                 // Dereference the PDP Entry
486                                 if( (addr + (1 << PDIR_SHIFT)) >> PDP_SHIFT != (addr >> PDP_SHIFT) ) {
487                                         MM_DerefPhys( PAGEMAPLVL4(addr >> PDP_SHIFT) & PADDR_MASK );
488                                         PAGEDIRPTR(addr >> PDP_SHIFT) = 0;
489                                 }
490                         }
491                         else {
492                                 addr += 1 << PDP_SHIFT;
493                                 continue;
494                         }
495                         // Dereference the PML4 Entry
496                         if( (addr + (1 << PDP_SHIFT)) >> PML4_SHIFT != (addr >> PML4_SHIFT) ) {
497                                 MM_DerefPhys( PAGEMAPLVL4(addr >> PML4_SHIFT) & PADDR_MASK );
498                                 PAGEMAPLVL4(addr >> PML4_SHIFT) = 0;
499                         }
500                 }
501                 else {
502                         addr += (tVAddr)1 << PML4_SHIFT;
503                         continue;
504                 }
505         }
506 }
507
508 tVAddr MM_NewWorkerStack(void)
509 {
510         return 0;
511 }
512
513 tVAddr MM_NewKStack(void)
514 {
515         return 0;
516 }

UCC git Repository :: git.ucc.asn.au