Kernel/x86_64 - Fixed recursive page fault, wasn't enabling the NX bit
[tpg/acess2.git] / Kernel / arch / x86_64 / mm_virt.c
1 /*
2  * Acess2 x86_64 Port
3  * 
4  * Virtual Memory Manager
5  */
6 #define DEBUG   0
7 #include <acess.h>
8 #include <mm_virt.h>
9 #include <threads_int.h>
10 #include <proc.h>
11
12 // === CONSTANTS ===
13 #define PHYS_BITS       52      // TODO: Move out
14 #define VIRT_BITS       48
15
16 #define PML4_SHIFT      39
17 #define PDP_SHIFT       30
18 #define PDIR_SHIFT      21
19 #define PTAB_SHIFT      12
20
21 #define PADDR_MASK      0x7FFFFFFF##FFFFF000
22 #define PAGE_MASK       ((1LL << 36)-1)
23 #define TABLE_MASK      ((1LL << 27)-1)
24 #define PDP_MASK        ((1LL << 18)-1)
25 #define PML4_MASK       ((1LL << 9)-1)
26
27 #define PF_PRESENT      0x001
28 #define PF_WRITE        0x002
29 #define PF_USER         0x004
30 #define PF_LARGE        0x080
31 #define PF_GLOBAL       0x100
32 #define PF_COW          0x200
33 #define PF_PAGED        0x400
34 #define PF_NX           0x80000000##00000000
35
36 // === MACROS ===
37 #define PAGETABLE(idx)  (*((Uint64*)MM_FRACTAL_BASE+((idx)&PAGE_MASK)))
38 #define PAGEDIR(idx)    PAGETABLE((MM_FRACTAL_BASE>>12)+((idx)&TABLE_MASK))
39 #define PAGEDIRPTR(idx) PAGEDIR((MM_FRACTAL_BASE>>21)+((idx)&PDP_MASK))
40 #define PAGEMAPLVL4(idx)        PAGEDIRPTR((MM_FRACTAL_BASE>>30)+((idx)&PML4_MASK))
41
42 #define TMPCR3()        PAGEMAPLVL4(MM_TMPFRAC_BASE>>39)
43 #define TMPTABLE(idx)   (*((Uint64*)MM_TMPFRAC_BASE+((idx)&PAGE_MASK)))
44 #define TMPDIR(idx)     PAGETABLE((MM_TMPFRAC_BASE>>12)+((idx)&TABLE_MASK))
45 #define TMPDIRPTR(idx)  PAGEDIR((MM_TMPFRAC_BASE>>21)+((idx)&PDP_MASK))
46 #define TMPMAPLVL4(idx) PAGEDIRPTR((MM_TMPFRAC_BASE>>30)+((idx)&PML4_MASK))
47
48 #define INVLPG(__addr)  __asm__ __volatile__ ("invlpg (%0)"::"r"(__addr))
49 #define INVLPG_ALL()    __asm__ __volatile__ ("mov %cr3,%rax;\n\tmov %rax,%cr3;")
50 #define INVLPG_GLOBAL() __asm__ __volatile__ ("mov %cr4,%rax;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4;\n\txorl $0x80, %eax;\n\tmov %rax,%cr4")
51
52 // === CONSTS ===
53 //tPAddr        * const gaPageTable = MM_FRACTAL_BASE;
54
55 // === IMPORTS ===
56 extern void     Error_Backtrace(Uint IP, Uint BP);
57 extern tPAddr   gInitialPML4[512];
58 extern void     Threads_SegFault(tVAddr Addr);
59 extern char     _UsertextBase[];
60
61 // === PROTOTYPES ===
62 void    MM_InitVirt(void);
63 //void  MM_FinishVirtualInit(void);
64 void    MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable );
65  int    MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
66 void    MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected);
67 void    MM_DumpTables(tVAddr Start, tVAddr End);
68  int    MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer);
69  int    MM_MapEx(tVAddr VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge);
70 // int  MM_Map(tVAddr VAddr, tPAddr PAddr);
71 void    MM_Unmap(tVAddr VAddr);
72 void    MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts);
73 void    MM_ClearUser(void);
74  int    MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags);
75
76 // === GLOBALS ===
77 tMutex  glMM_TempFractalLock;
78 tPAddr  gMM_ZeroPage;
79
80 // === CODE ===
81 void MM_InitVirt(void)
82 {
83 //      Log_Debug("MMVirt", "&PAGEMAPLVL4(0) = %p", &PAGEMAPLVL4(0));
84 //      MM_DumpTables(0, -1L);
85 }
86
87 void MM_FinishVirtualInit(void)
88 {
89         PAGEMAPLVL4(0) = 0;
90 }
91
92 /**
93  * \brief Clone a page from an entry
94  * \param Ent   Pointer to the entry in the PML4/PDP/PD/PT
95  * \param NextLevel     Pointer to contents of the entry
96  * \param Addr  Dest address
97  * \note Used in COW
98  */
99 void MM_int_ClonePageEnt( Uint64 *Ent, void *NextLevel, tVAddr Addr, int bTable )
100 {
101         tPAddr  curpage = *Ent & PADDR_MASK; 
102         if( MM_GetRefCount( curpage ) <= 0 ) {
103                 Log_KernelPanic("MMVirt", "Page %P still marked COW, but unreferenced", curpage);
104         }
105         if( MM_GetRefCount( curpage ) == 1 )
106         {
107                 *Ent &= ~PF_COW;
108                 *Ent |= PF_PRESENT|PF_WRITE;
109 //              Log_Debug("MMVirt", "COW ent at %p (%p) only %P", Ent, NextLevel, curpage);
110         }
111         else
112         {
113                 void    *tmp;
114                 tPAddr  paddr;
115                 
116                 if( !(paddr = MM_AllocPhys()) ) {
117                         Threads_SegFault(Addr);
118                         return ;
119                 }
120
121                 ASSERT(paddr != curpage);
122                         
123                 tmp = (void*)MM_MapTemp(paddr);
124                 memcpy( tmp, NextLevel, 0x1000 );
125                 MM_FreeTemp( (tVAddr)tmp );
126                 
127 //              Log_Debug("MMVirt", "COW ent at %p (%p) from %P to %P", Ent, NextLevel, curpage, paddr);
128
129                 MM_DerefPhys( curpage );
130                 *Ent &= PF_USER;
131                 *Ent |= paddr|PF_PRESENT|PF_WRITE;
132         }
133         INVLPG( (tVAddr)NextLevel );
134         
135         // Mark COW on pages
136         if(bTable) 
137         {
138                 Uint64  *dp = NextLevel;
139                  int    i;
140                 for( i = 0; i < 512; i ++ )
141                 {
142                         if( !(dp[i] & PF_PRESENT) )     continue;
143                         MM_RefPhys( dp[i] & PADDR_MASK );
144                         if( dp[i] & PF_WRITE ) {
145                                 dp[i] &= ~PF_WRITE;
146                                 dp[i] |= PF_COW;
147                         }
148                 }
149         }
150 }
151
152 /*
153  * \brief Called on a page fault
154  */
155 int MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
156 {
157 //      Log_Debug("MMVirt", "Addr = %p, ErrorCode = %x", Addr, ErrorCode);
158
159         // Catch reserved bits first
160         if( ErrorCode & 0x8 )
161         {
162                 Log_Warning("MMVirt", "Reserved bits trashed!");
163                 Log_Warning("MMVirt", "PML4 Ent   = %P", PAGEMAPLVL4(Addr>>39));
164                 if( !(PAGEMAPLVL4(Addr>>39) & PF_PRESENT) )     goto print_done;
165                 Log_Warning("MMVirt", "PDP Ent    = %P", PAGEDIRPTR(Addr>>30));
166                 if( !(PAGEDIRPTR(Addr>>30) & PF_PRESENT) )      goto print_done;
167                 Log_Warning("MMVirt", "PDir Ent   = %P", PAGEDIR(Addr>>21));
168                 if( !(PAGEDIR(Addr>>21) & PF_PRESENT) ) goto print_done;
169                 Log_Warning("MMVirt", "PTable Ent = %P", PAGETABLE(Addr>>12));
170                 if( !(PAGETABLE(Addr>>12) & PF_PRESENT) )       goto print_done;
171         print_done:
172                 
173                 for(;;);
174         }
175
176         // TODO: Implement Copy-on-Write
177         #if 1
178         if( PAGEMAPLVL4(Addr>>39) & PF_PRESENT
179          && PAGEDIRPTR (Addr>>30) & PF_PRESENT
180          && PAGEDIR    (Addr>>21) & PF_PRESENT
181          && PAGETABLE  (Addr>>12) & PF_PRESENT )
182         {
183                 // PML4 Entry
184                 if( PAGEMAPLVL4(Addr>>39) & PF_COW )
185                 {
186                         tPAddr  *dp = &PAGEDIRPTR((Addr>>39)*512);
187                         MM_int_ClonePageEnt( &PAGEMAPLVL4(Addr>>39), dp, Addr, 1 );
188 //                      MM_DumpTables(Addr>>39 << 39, (((Addr>>39) + 1) << 39) - 1);
189                 }
190                 // PDP Entry
191                 if( PAGEDIRPTR(Addr>>30) & PF_COW )
192                 {
193                         tPAddr  *dp = &PAGEDIR( (Addr>>30)*512 );
194                         MM_int_ClonePageEnt( &PAGEDIRPTR(Addr>>30), dp, Addr, 1 );
195 //                      MM_DumpTables(Addr>>30 << 30, (((Addr>>30) + 1) << 30) - 1);
196                 }
197                 // PD Entry
198                 if( PAGEDIR(Addr>>21) & PF_COW )
199                 {
200                         tPAddr  *dp = &PAGETABLE( (Addr>>21)*512 );
201                         MM_int_ClonePageEnt( &PAGEDIR(Addr>>21), dp, Addr, 1 );
202 //                      MM_DumpTables(Addr>>21 << 21, (((Addr>>21) + 1) << 21) - 1);
203                 }
204                 // PT Entry
205                 if( PAGETABLE(Addr>>12) & PF_COW )
206                 {
207                         MM_int_ClonePageEnt( &PAGETABLE(Addr>>12), (void*)(Addr & ~0xFFF), Addr, 0 );
208                         INVLPG( Addr & ~0xFFF );
209                         return 0;
210                 }
211         }
212         #endif
213         
214         // If it was a user, tell the thread handler
215         if(ErrorCode & 4) {
216                 Warning("User %s %s memory%s",
217                         (ErrorCode&2?"write to":"read from"),
218                         (ErrorCode&1?"bad/locked":"non-present"),
219                         (ErrorCode&16?" (Instruction Fetch)":"")
220                         );
221                 Warning("User Pagefault: Instruction at %04x:%p accessed %p",
222                         Regs->CS, Regs->RIP, Addr);
223                 __asm__ __volatile__ ("sti");   // Restart IRQs
224                 Error_Backtrace(Regs->RIP, Regs->RBP);
225                 Threads_SegFault(Addr);
226                 return 0;
227         }
228         
229         // Kernel #PF
230         Debug_KernelPanic();
231         // -- Check Error Code --
232         if(ErrorCode & 8)
233                 Warning("Reserved Bits Trashed!");
234         else
235         {
236                 Warning("Kernel %s %s memory%s",
237                         (ErrorCode&2?"write to":"read from"),
238                         (ErrorCode&1?"bad/locked":"non-present"),
239                         (ErrorCode&16?" (Instruction Fetch)":"")
240                         );
241         }
242         
243         Log("Thread %i - Code at %p accessed %p", Threads_GetTID(), Regs->RIP, Addr);
244         // Print Stack Backtrace
245         Error_Backtrace(Regs->RIP, Regs->RBP);
246         
247         MM_DumpTables(0, -1);
248
249         return 1;       
250 }
251
252 void MM_int_DumpTablesEnt(tVAddr RangeStart, size_t Length, tPAddr Expected)
253 {
254         #define CANOICAL(addr)  ((addr)&0x800000000000?(addr)|0xFFFF000000000000:(addr))
255         LogF("%016llx => ", CANOICAL(RangeStart));
256 //      LogF("%6llx %6llx %6llx %016llx => ",
257 //              MM_GetPhysAddr( (tVAddr)&PAGEDIRPTR(RangeStart>>30) ),
258 //              MM_GetPhysAddr( (tVAddr)&PAGEDIR(RangeStart>>21) ),
259 //              MM_GetPhysAddr( (tVAddr)&PAGETABLE(RangeStart>>12) ),
260 //              CANOICAL(RangeStart)
261 //              );
262         if( gMM_ZeroPage && (PAGETABLE(RangeStart>>12) & PADDR_MASK) == gMM_ZeroPage )
263                 LogF("%13s", "zero" );
264         else
265                 LogF("%13llx", PAGETABLE(RangeStart>>12) & PADDR_MASK );
266         LogF(" : 0x%6llx (%c%c%c%c)\r\n",
267                 Length,
268                 (Expected & PF_PAGED ? 'p' : '-'),
269                 (Expected & PF_COW ? 'C' : '-'),
270                 (Expected & PF_USER ? 'U' : '-'),
271                 (Expected & PF_WRITE ? 'W' : '-')
272                 );
273         #undef CANOICAL
274 }
275
276 /**
277  * \brief Dumps the layout of the page tables
278  */
279 void MM_DumpTables(tVAddr Start, tVAddr End)
280 {
281         const tPAddr    CHANGEABLE_BITS = ~(PF_PRESENT|PF_WRITE|PF_USER|PF_COW|PF_PAGED) & 0xFFF;
282         const tPAddr    MASK = ~CHANGEABLE_BITS;        // Physical address and access bits
283         tVAddr  rangeStart = 0;
284         tPAddr  expected = CHANGEABLE_BITS;     // CHANGEABLE_BITS is used because it's not a vaild value
285         tVAddr  curPos;
286         Uint    page;
287         
288         Log("Table Entries: (%p to %p)", Start, End);
289         
290         End &= (1L << 48) - 1;
291         
292         Start >>= 12;   End >>= 12;
293         
294         for(page = Start, curPos = Start<<12;
295                 page < End;
296                 curPos += 0x1000, page++)
297         {
298                 //Debug("&PAGEMAPLVL4(%i page>>27) = %p", page>>27, &PAGEMAPLVL4(page>>27));
299                 //Debug("&PAGEDIRPTR(%i page>>18) = %p", page>>18, &PAGEDIRPTR(page>>18));
300                 //Debug("&PAGEDIR(%i page>>9) = %p", page>>9, &PAGEDIR(page>>9));
301                 //Debug("&PAGETABLE(%i page) = %p", page, &PAGETABLE(page));
302                 
303                 // End of a range
304                 if(!(PAGEMAPLVL4(page>>27) & PF_PRESENT)
305                 || !(PAGEDIRPTR(page>>18) & PF_PRESENT)
306                 || !(PAGEDIR(page>>9) & PF_PRESENT)
307                 || !(PAGETABLE(page) & PF_PRESENT)
308                 || (PAGETABLE(page) & MASK) != expected)
309                 {                       
310                         if(expected != CHANGEABLE_BITS)
311                         {
312                                 MM_int_DumpTablesEnt( rangeStart, curPos - rangeStart, expected );
313                                 expected = CHANGEABLE_BITS;
314                         }
315                         
316                         if( curPos == 0x800000000000L )
317                                 curPos = 0xFFFF800000000000L;
318                 
319                         if( !(PAGEMAPLVL4(page>>27) & PF_PRESENT) ) {
320                                 page += (1 << 27) - 1;
321                                 curPos += (1L << 39) - 0x1000;
322                                 continue;
323                         }
324                         if( !(PAGEDIRPTR(page>>18) & PF_PRESENT) ) {
325                                 page += (1 << 18) - 1;
326                                 curPos += (1L << 30) - 0x1000;
327                                 continue;
328                         }
329                         if( !(PAGEDIR(page>>9) & PF_PRESENT) ) {
330                                 page += (1 << 9) - 1;
331                                 curPos += (1L << 21) - 0x1000;
332                                 continue;
333                         }
334                         if( !(PAGETABLE(page) & PF_PRESENT) )   continue;
335                         
336                         expected = (PAGETABLE(page) & MASK);
337                         rangeStart = curPos;
338                 }
339                 if(gMM_ZeroPage && (expected & PADDR_MASK) == gMM_ZeroPage )
340                         expected = expected;
341                 else if(expected != CHANGEABLE_BITS)
342                         expected += 0x1000;
343         }
344         
345         if(expected != CHANGEABLE_BITS) {
346                 MM_int_DumpTablesEnt( rangeStart, curPos - rangeStart, expected );
347                 expected = 0;
348         }
349 }
350
351 /**
352  * \brief Get a pointer to a page entry
353  * \param Addr  Virtual Address
354  * \param bTemp Use the Temporary fractal mapping
355  * \param bAllocate     Allocate entries
356  * \param bLargePage    Request a large page
357  * \param Pointer       Location to place the calculated pointer
358  * \return Page size, or -ve on error
359  */
360 int MM_GetPageEntryPtr(tVAddr Addr, BOOL bTemp, BOOL bAllocate, BOOL bLargePage, tPAddr **Pointer)
361 {
362         tPAddr  *pmlevels[4];
363         tPAddr  tmp;
364          int    i, size;
365         
366         #define BITMASK(bits)   ( (1LL << (bits))-1 )
367
368         if( bTemp )
369         {
370                 pmlevels[3] = &TMPTABLE(0);     // Page Table
371                 pmlevels[2] = &TMPDIR(0);       // PDIR
372                 pmlevels[1] = &TMPDIRPTR(0);    // PDPT
373                 pmlevels[0] = &TMPMAPLVL4(0);   // PML4
374         }
375         else
376         {
377                 pmlevels[3] = (void*)MM_FRACTAL_BASE;   // Page Table
378                 pmlevels[2] = &pmlevels[3][(MM_FRACTAL_BASE>>12)&BITMASK(VIRT_BITS-12)];        // PDIR
379                 pmlevels[1] = &pmlevels[2][(MM_FRACTAL_BASE>>21)&BITMASK(VIRT_BITS-21)];        // PDPT
380                 pmlevels[0] = &pmlevels[1][(MM_FRACTAL_BASE>>30)&BITMASK(VIRT_BITS-30)];        // PML4
381         }
382         
383         // Mask address
384         Addr &= (1ULL << 48)-1;
385         
386         for( size = 39, i = 0; size > 12; size -= 9, i ++ )
387         {
388                 Uint64  *ent = &pmlevels[i][Addr >> size];
389 //              INVLPG( &pmlevels[i][ (Addr >> ADDR_SIZES[i]) & 
390                 
391                 // Check for a free large page slot
392                 // TODO: Better support with selectable levels
393                 if( (Addr & ((1ULL << size)-1)) == 0 && bLargePage )
394                 {
395                         if(Pointer)     *Pointer = ent;
396                         return size;
397                 }
398                 // Allocate an entry if required
399                 if( !(*ent & PF_PRESENT) )
400                 {
401                         if( !bAllocate )        return -4;      // If allocation is not requested, error
402                         if( !(tmp = MM_AllocPhys()) )   return -2;
403                         *ent = tmp | 3;
404                         if( Addr < 0x800000000000 )
405                                 *ent |= PF_USER;
406                         INVLPG( &pmlevels[i+1][ (Addr>>size)*512 ] );
407                         memset( &pmlevels[i+1][ (Addr>>size)*512 ], 0, 0x1000 );
408                         LOG("Init PML%i ent 0x%x %p with %P", 4 - i,
409                                 Addr>>size, (Addr>>size) << size, tmp);
410                 }
411                 // Catch large pages
412                 else if( *ent & PF_LARGE )
413                 {
414                         // Alignment
415                         if( (Addr & ((1ULL << size)-1)) != 0 )  return -3;
416                         if(Pointer)     *Pointer = ent;
417                         return size;    // Large page warning
418                 }
419         }
420         
421         // And, set the page table entry
422         if(Pointer)     *Pointer = &pmlevels[i][Addr >> size];
423         return size;
424 }
425
426 /**
427  * \brief Map a physical page to a virtual one
428  * \param VAddr Target virtual address
429  * \param PAddr Physical address of page
430  * \param bTemp Use tempoary mappings
431  * \param bLarge        Treat as a large page
432  */
433 int MM_MapEx(tVAddr VAddr, tPAddr PAddr, BOOL bTemp, BOOL bLarge)
434 {
435         tPAddr  *ent;
436          int    rv;
437         
438         ENTER("pVAddr PPAddr", VAddr, PAddr);
439         
440         // Get page pointer (Allow allocating)
441         rv = MM_GetPageEntryPtr(VAddr, bTemp, 1, bLarge, &ent);
442         if(rv < 0)      LEAVE_RET('i', 0);
443         
444         if( *ent & 1 )  LEAVE_RET('i', 0);
445         
446         *ent = PAddr | 3;
447
448         if( VAddr < 0x800000000000 )
449                 *ent |= PF_USER;
450
451         INVLPG( VAddr );
452
453         LEAVE('i', 1);  
454         return 1;
455 }
456
457 /**
458  * \brief Map a physical page to a virtual one
459  * \param VAddr Target virtual address
460  * \param PAddr Physical address of page
461  */
462 int MM_Map(tVAddr VAddr, tPAddr PAddr)
463 {
464         return MM_MapEx(VAddr, PAddr, 0, 0);
465 }
466
467 /**
468  * \brief Removed a mapped page
469  */
470 void MM_Unmap(tVAddr VAddr)
471 {
472         // Check PML4
473         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )   return ;
474         // Check PDP
475         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )    return ;
476         // Check Page Dir
477         if( !(PAGEDIR(VAddr >> 21) & 1) )       return ;
478
479         PAGETABLE(VAddr >> PTAB_SHIFT) = 0;
480         INVLPG( VAddr );
481 }
482
483 /**
484  * \brief Allocate a block of memory at the specified virtual address
485  */
486 tPAddr MM_Allocate(tVAddr VAddr)
487 {
488         tPAddr  ret;
489         
490         ENTER("xVAddr", VAddr);
491         
492         // Ensure the tables are allocated before the page (keeps things neat)
493         MM_GetPageEntryPtr(VAddr, 0, 1, 0, NULL);
494         
495         // Allocate the page
496         ret = MM_AllocPhys();
497         LOG("ret = %x", ret);
498         if(!ret)        LEAVE_RET('i', 0);
499         
500         if( !MM_Map(VAddr, ret) )
501         {
502                 Warning("MM_Allocate: Unable to map. Strange, we should have errored earlier");
503                 MM_DerefPhys(ret);
504                 LEAVE('i');
505                 return 0;
506         }
507         
508         LEAVE('X', ret);
509         return ret;
510 }
511
512 tPAddr MM_AllocateZero(tVAddr VAddr)
513 {
514         tPAddr  ret = gMM_ZeroPage;
515         
516         MM_GetPageEntryPtr(VAddr, 0, 1, 0, NULL);
517
518         if(!gMM_ZeroPage) {
519                 ret = gMM_ZeroPage = MM_AllocPhys();
520                 MM_RefPhys(ret);        // Don't free this please
521                 MM_Map(VAddr, ret);
522                 memset((void*)VAddr, 0, 0x1000);
523         }
524         else {
525                 MM_Map(VAddr, ret);
526         }
527         MM_RefPhys(ret);        // Refernce for this map
528         MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW);
529         return ret;
530 }
531
532 /**
533  * \brief Deallocate a page at a virtual address
534  */
535 void MM_Deallocate(tVAddr VAddr)
536 {
537         tPAddr  phys;
538         
539         phys = MM_GetPhysAddr(VAddr);
540         if(!phys)       return ;
541         
542         MM_Unmap(VAddr);
543         
544         MM_DerefPhys(phys);
545 }
546
547 /**
548  * \brief Get the page table entry of a virtual address
549  * \param Addr  Virtual Address
550  * \param Phys  Location to put the physical address
551  * \param Flags Flags on the entry (set to zero if unmapped)
552  * \return Size of the entry (in address bits) - 12 = 4KiB page
553  */
554 int MM_GetPageEntry(tVAddr Addr, tPAddr *Phys, Uint *Flags)
555 {
556         tPAddr  *ptr;
557          int    ret;
558         
559         if(!Phys || !Flags)     return 0;
560         
561         ret = MM_GetPageEntryPtr(Addr, 0, 0, 0, &ptr);
562         if( ret < 0 )   return 0;
563         
564         *Phys = *ptr & PADDR_MASK;
565         *Flags = *ptr & 0xFFF;
566         return ret;
567 }
568
569 /**
570  * \brief Get the physical address of a virtual location
571  */
572 tPAddr MM_GetPhysAddr(tVAddr Addr)
573 {
574         tPAddr  *ptr;
575          int    ret;
576         
577         ret = MM_GetPageEntryPtr(Addr, 0, 0, 0, &ptr);
578         if( ret < 0 )   return 0;
579         
580         if( !(*ptr & 1) )       return 0;
581         
582         return (*ptr & PADDR_MASK) | (Addr & 0xFFF);
583 }
584
585 /**
586  * \brief Sets the flags on a page
587  */
588 void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
589 {
590         tPAddr  *ent;
591          int    rv;
592         
593         // Get pointer
594         rv = MM_GetPageEntryPtr(VAddr, 0, 0, 0, &ent);
595         if(rv < 0)      return ;
596         
597         // Ensure the entry is valid
598         if( !(*ent & 1) )       return ;
599         
600         // Read-Only
601         if( Mask & MM_PFLAG_RO )
602         {
603                 if( Flags & MM_PFLAG_RO ) {
604                         *ent &= ~PF_WRITE;
605                 }
606                 else {
607                         *ent |= PF_WRITE;
608                 }
609         }
610         
611         // Kernel
612         if( Mask & MM_PFLAG_KERNEL )
613         {
614                 if( Flags & MM_PFLAG_KERNEL ) {
615                         *ent &= ~PF_USER;
616                 }
617                 else {
618                         *ent |= PF_USER;
619                 }
620         }
621         
622         // Copy-On-Write
623         if( Mask & MM_PFLAG_COW )
624         {
625                 if( Flags & MM_PFLAG_COW ) {
626                         *ent &= ~PF_WRITE;
627                         *ent |= PF_COW;
628                 }
629                 else {
630                         *ent &= ~PF_COW;
631                         *ent |= PF_WRITE;
632                 }
633         }
634         
635         // Execute
636         if( Mask & MM_PFLAG_EXEC )
637         {
638                 if( Flags & MM_PFLAG_EXEC ) {
639                         *ent &= ~PF_NX;
640                 }
641                 else {
642                         *ent |= PF_NX;
643                 }
644         }
645 }
646
647 /**
648  * \brief Get the flags applied to a page
649  */
650 Uint MM_GetFlags(tVAddr VAddr)
651 {
652         tPAddr  *ent;
653          int    rv, ret = 0;
654         
655         rv = MM_GetPageEntryPtr(VAddr, 0, 0, 0, &ent);
656         if(rv < 0)      return 0;
657         
658         if( !(*ent & 1) )       return 0;
659         
660         // Read-Only
661         if( !(*ent & PF_WRITE) )        ret |= MM_PFLAG_RO;
662         // Kernel
663         if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL;
664         // Copy-On-Write
665         if( *ent & PF_COW )     ret |= MM_PFLAG_COW;    
666         // Execute
667         if( !(*ent & PF_NX) )   ret |= MM_PFLAG_EXEC;
668         
669         return ret;
670 }
671
672 // --- Hardware Mappings ---
673 /**
674  * \brief Map a range of hardware pages
675  */
676 tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
677 {
678         tVAddr  ret;
679          int    num;
680         
681         //TODO: Add speedups (memory of first possible free)
682         for( ret = MM_HWMAP_BASE; ret < MM_HWMAP_TOP; ret += 0x1000 )
683         {
684                 for( num = Number; num -- && ret < MM_HWMAP_TOP; ret += 0x1000 )
685                 {
686                         if( MM_GetPhysAddr(ret) != 0 )  break;
687                 }
688                 if( num >= 0 )  continue;
689                 
690 //              Log_Debug("MMVirt", "Mapping %i pages to %p (base %P)", Number, ret-Number*0x1000, PAddr);
691
692                 PAddr += 0x1000 * Number;
693                 
694                 while( Number -- )
695                 {
696                         ret -= 0x1000;
697                         PAddr -= 0x1000;
698                         MM_Map(ret, PAddr);
699                         MM_RefPhys(PAddr);
700                 }
701                 
702                 return ret;
703         }
704         
705         Log_Error("MM", "MM_MapHWPages - No space for %i pages", Number);
706         return 0;
707 }
708
709 /**
710  * \brief Free a range of hardware pages
711  */
712 void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
713 {
714 //      Log_KernelPanic("MM", "TODO: Implement MM_UnmapHWPages");
715         while( Number -- )
716         {
717                 MM_DerefPhys( MM_GetPhysAddr(VAddr) );
718                 MM_Unmap(VAddr);
719                 VAddr += 0x1000;
720         }
721 }
722
723
724 /**
725  * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
726  * \brief Allocates DMA physical memory
727  * \param Pages Number of pages required
728  * \param MaxBits       Maximum number of bits the physical address can have
729  * \param PhysAddr      Pointer to the location to place the physical address allocated
730  * \return Virtual address allocate
731  */
732 tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
733 {
734         tPAddr  phys;
735         tVAddr  ret;
736         
737         // Sanity Check
738         if(MaxBits < 12 || !PhysAddr)   return 0;
739         
740         // Fast Allocate
741         if(Pages == 1 && MaxBits >= PHYS_BITS)
742         {
743                 phys = MM_AllocPhys();
744                 *PhysAddr = phys;
745                 ret = MM_MapHWPages(phys, 1);
746                 MM_DerefPhys(phys);
747                 return ret;
748         }
749         
750         // Slow Allocate
751         phys = MM_AllocPhysRange(Pages, MaxBits);
752         // - Was it allocated?
753         if(phys == 0)   return 0;
754         
755         // Allocated successfully, now map
756         ret = MM_MapHWPages(phys, Pages);
757         // MapHWPages references the pages, so deref them back down to 1
758         for(;Pages--;phys+=0x1000)
759                 MM_DerefPhys(phys);
760         if( ret == 0 ) {
761                 // If it didn't map, free then return 0
762                 return 0;
763         }
764         
765         *PhysAddr = phys;
766         return ret;
767 }
768
769 // --- Tempory Mappings ---
770 tVAddr MM_MapTemp(tPAddr PAddr)
771 {
772         const int max_slots = (MM_TMPMAP_END - MM_TMPMAP_BASE) / PAGE_SIZE;
773         tVAddr  ret = MM_TMPMAP_BASE;
774          int    i;
775         
776         for( i = 0; i < max_slots; i ++, ret += PAGE_SIZE )
777         {
778                 tPAddr  *ent;
779                 if( MM_GetPageEntryPtr( ret, 0, 1, 0, &ent) < 0 ) {
780                         continue ;
781                 }
782
783                 if( *ent & 1 )
784                         continue ;
785
786                 *ent = PAddr | 3;
787                 MM_RefPhys(PAddr);
788                 INVLPG(ret);
789                 return ret;
790         }
791         return 0;
792 }
793
794 void MM_FreeTemp(tVAddr VAddr)
795 {
796         MM_Deallocate(VAddr);
797         return ;
798 }
799
800
801 // --- Address Space Clone --
802 tPAddr MM_Clone(void)
803 {
804         tPAddr  ret;
805          int    i;
806         tVAddr  kstackbase;
807
808         // #1 Create a copy of the PML4
809         ret = MM_AllocPhys();
810         if(!ret)        return 0;
811         
812         // #2 Alter the fractal pointer
813         Mutex_Acquire(&glMM_TempFractalLock);
814         TMPCR3() = ret | 3;
815         INVLPG_ALL();
816         
817         // #3 Set Copy-On-Write to all user pages
818         for( i = 0; i < 256; i ++)
819         {
820                 if( PAGEMAPLVL4(i) & PF_WRITE ) {
821                         PAGEMAPLVL4(i) |= PF_COW;
822                         PAGEMAPLVL4(i) &= ~PF_WRITE;
823                 }
824
825                 TMPMAPLVL4(i) = PAGEMAPLVL4(i);
826 //              Log_Debug("MM", "TMPMAPLVL4(%i) = 0x%016llx", i, TMPMAPLVL4(i));
827                 if( !(TMPMAPLVL4(i) & PF_PRESENT) )     continue ;
828                 
829                 MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK );
830         }
831         
832         // #4 Map in kernel pages
833         for( i = 256; i < 512; i ++ )
834         {
835                 // Skip addresses:
836                 // 320 0xFFFFA....      - Kernel Stacks
837                 if( i == 320 )  continue;
838                 // 509 0xFFFFFE0..      - Fractal mapping
839                 if( i == 508 )  continue;
840                 // 510 0xFFFFFE8..      - Temp fractal mapping
841                 if( i == 509 )  continue;
842                 
843                 TMPMAPLVL4(i) = PAGEMAPLVL4(i);
844                 if( TMPMAPLVL4(i) & 1 )
845                         MM_RefPhys( TMPMAPLVL4(i) & PADDR_MASK );
846         }
847
848         // Mark Per-Process data as COW
849         TMPMAPLVL4(MM_PPD_BASE>>39) |= PF_COW;
850         TMPMAPLVL4(MM_PPD_BASE>>39) &= ~PF_WRITE;
851         
852         // #5 Set fractal mapping
853         TMPMAPLVL4(MM_FRACTAL_BASE>>39) = ret | 3;      // Main
854         TMPMAPLVL4(MM_TMPFRAC_BASE>>39) = 0;    // Temp
855         
856         // #6 Create kernel stack
857         //  tThread->KernelStack is the top
858         //  There is 1 guard page below the stack
859         kstackbase = Proc_GetCurThread()->KernelStack - KERNEL_STACK_SIZE;
860
861         // Clone stack
862         TMPMAPLVL4(MM_KSTACK_BASE >> PML4_SHIFT) = 0;
863         for( i = 1; i < KERNEL_STACK_SIZE/0x1000; i ++ )
864         {
865                 tPAddr  phys = MM_AllocPhys();
866                 tVAddr  tmpmapping;
867                 MM_MapEx(kstackbase+i*0x1000, phys, 1, 0);
868                 
869                 tmpmapping = MM_MapTemp(phys);
870                 if( MM_GetPhysAddr( kstackbase+i*0x1000 ) )
871                         memcpy((void*)tmpmapping, (void*)(kstackbase+i*0x1000), 0x1000);
872                 else
873                         memset((void*)tmpmapping, 0, 0x1000);
874 //              if( i == 0xF )
875 //                      Debug_HexDump("MM_Clone: *tmpmapping = ", (void*)tmpmapping, 0x1000);
876                 MM_FreeTemp(tmpmapping);
877         }
878         
879 //      MAGIC_BREAK();
880
881         // #7 Return
882         TMPCR3() = 0;
883         INVLPG_ALL();
884         Mutex_Release(&glMM_TempFractalLock);
885 //      Log("MM_Clone: RETURN %P", ret);
886         return ret;
887 }
888
889 void MM_int_ClearTableLevel(tVAddr VAddr, int LevelBits, int MaxEnts)
890 {
891         Uint64  * const table_bases[] = {&PAGETABLE(0), &PAGEDIR(0), &PAGEDIRPTR(0), &PAGEMAPLVL4(0)};
892         Uint64  *table = table_bases[(LevelBits-12)/9] + (VAddr >> LevelBits);
893          int    i;
894 //      Log("MM_int_ClearTableLevel: (VAddr=%p, LevelBits=%i, MaxEnts=%i)", VAddr, LevelBits, MaxEnts);
895         for( i = 0; i < MaxEnts; i ++ )
896         {
897                 // Skip non-present tables
898                 if( !(table[i] & PF_PRESENT) ) {
899                         table[i] = 0;
900                         continue ;
901                 }
902         
903                 if( (table[i] & PF_COW) && MM_GetRefCount(table[i] & PADDR_MASK) > 1 ) {
904                         MM_DerefPhys(table[i] & PADDR_MASK);
905                         table[i] = 0;
906                         continue ;
907                 }
908                 // Clear table contents (if it is a table)
909                 if( LevelBits > 12 )
910                         MM_int_ClearTableLevel(VAddr + ((tVAddr)i << LevelBits), LevelBits-9, 512);
911                 MM_DerefPhys(table[i] & PADDR_MASK);
912                 table[i] = 0;
913         }
914 }
915
916 void MM_ClearUser(void)
917 {
918         MM_int_ClearTableLevel(0, 39, 256);     
919 }
920
921 tVAddr MM_NewWorkerStack(void *StackData, size_t StackSize)
922 {
923         tVAddr  ret;
924          int    i;
925         
926         // #1 Set temp fractal to PID0
927         Mutex_Acquire(&glMM_TempFractalLock);
928         TMPCR3() = ((tPAddr)gInitialPML4 - KERNEL_BASE) | 3;
929         
930         // #2 Scan for a free stack addresss < 2^47
931         for(ret = 0x100000; ret < (1ULL << 47); ret += KERNEL_STACK_SIZE)
932         {
933                 tPAddr  *ptr;
934                 if( MM_GetPageEntryPtr(ret, 1, 0, 0, &ptr) <= 0 )       break;
935                 if( !(*ptr & 1) )       break;
936         }
937         if( ret >= (1ULL << 47) ) {
938                 Mutex_Release(&glMM_TempFractalLock);
939                 return 0;
940         }
941         
942         // #3 Map all save the last page in the range
943         //    - This acts as as guard page, and doesn't cost us anything.
944         for( i = 0; i < KERNEL_STACK_SIZE/0x1000 - 1; i ++ )
945         {
946                 tPAddr  phys = MM_AllocPhys();
947                 if(!phys) {
948                         // TODO: Clean up
949                         Log_Error("MM", "MM_NewWorkerStack - Unable to allocate page");
950                         return 0;
951                 }
952                 MM_MapEx(ret + i*0x1000, phys, 1, 0);
953         }
954
955         if( StackSize > 0x1000 ) {
956                 Log_Error("MM", "MM_NewWorkerStack: StackSize(0x%x) > 0x1000, cbf handling", StackSize);
957         }
958         else {
959                 tPAddr  *ptr, paddr;
960                 tVAddr  tmp_addr;
961                 MM_GetPageEntryPtr(ret + i*0x1000, 1, 0, 0, &ptr);
962                 paddr = *ptr & ~0xFFF;
963                 tmp_addr = MM_MapTemp(paddr);
964                 memcpy( (void*)(tmp_addr + (0x1000 - StackSize)), StackData, StackSize );
965                 MM_FreeTemp(tmp_addr);
966         }
967         
968         Mutex_Release(&glMM_TempFractalLock);
969         
970         return ret + i*0x1000;
971 }
972
973 /**
974  * \brief Allocate a new kernel stack
975  */
976 tVAddr MM_NewKStack(void)
977 {
978         tVAddr  base = MM_KSTACK_BASE;
979         Uint    i;
980         for( ; base < MM_KSTACK_TOP; base += KERNEL_STACK_SIZE )
981         {
982                 if(MM_GetPhysAddr(base+KERNEL_STACK_SIZE-0x1000) != 0)
983                         continue;
984                 
985                 //Log("MM_NewKStack: Found one at %p", base + KERNEL_STACK_SIZE);
986                 for( i = 0x1000; i < KERNEL_STACK_SIZE; i += 0x1000)
987                 {
988                         if( !MM_Allocate(base+i) )
989                         {
990                                 Log_Warning("MM", "MM_NewKStack - Allocation failed");
991                                 for( i -= 0x1000; i; i -= 0x1000)
992                                         MM_Deallocate(base+i);
993                                 return 0;
994                         }
995                 }
996                 
997                 return base + KERNEL_STACK_SIZE;
998         }
999         Log_Warning("MM", "MM_NewKStack - No address space left\n");
1000         return 0;
1001 }

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