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

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