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

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