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

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