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

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