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

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