New kernel stack, fixing pmem alloc bug, updated to maintain free page positions
[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 <proc.h>
10
11 // === CONSTANTS ===
12 #define PML4_SHIFT      39
13 #define PDP_SHIFT       30
14 #define PDIR_SHIFT      21
15 #define PTAB_SHIFT      12
16
17 #define PADDR_MASK      0x7FFFFFFF##FFFFF000
18 #define PAGE_MASK       (((Uint)1 << 36)-1)
19 #define TABLE_MASK      (((Uint)1 << 27)-1)
20 #define PDP_MASK        (((Uint)1 << 18)-1)
21 #define PML4_MASK       (((Uint)1 << 9)-1)
22
23 #define PF_PRESENT      0x1
24 #define PF_WRITE        0x2
25 #define PF_USER         0x4
26 #define PF_COW          0x200
27 #define PF_PAGED        0x400
28 #define PF_NX           0x80000000##00000000
29
30 // === MACROS ===
31 #define PAGETABLE(idx)  (*((tPAddr*)MM_FRACTAL_BASE+((idx)&PAGE_MASK)))
32 #define PAGEDIR(idx)    PAGETABLE((MM_FRACTAL_BASE>>12)+((idx)&TABLE_MASK))
33 #define PAGEDIRPTR(idx) PAGEDIR((MM_FRACTAL_BASE>>21)+((idx)&PDP_MASK))
34 #define PAGEMAPLVL4(idx)        PAGEDIRPTR((MM_FRACTAL_BASE>>30)+((idx)&PML4_MASK))
35
36 #define INVLPG(__addr)  __asm__ __volatile__ ("invlpg (%0)"::"r"(__addr));
37
38 // === PROTOTYPES ===
39 void    MM_InitVirt(void);
40 void    MM_FinishVirtualInit(void);
41 void    MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
42 void    MM_DumpTables(tVAddr Start, tVAddr End);
43  int    MM_Map(tVAddr VAddr, tPAddr PAddr);
44
45 // === GLOBALS ===
46
47 // === CODE ===
48 void MM_InitVirt(void)
49 {
50 }
51
52 void MM_FinishVirtualInit(void)
53 {
54 }
55
56 /**
57  * \brief Called on a page fault
58  */
59 void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
60 {
61         // TODO: Copy on Write
62         #if 0
63         if( gaPageDir  [Addr>>22] & PF_PRESENT
64          && gaPageTable[Addr>>12] & PF_PRESENT
65          && gaPageTable[Addr>>12] & PF_COW )
66         {
67                 tPAddr  paddr;
68                 if(MM_GetRefCount( gaPageTable[Addr>>12] & ~0xFFF ) == 1)
69                 {
70                         gaPageTable[Addr>>12] &= ~PF_COW;
71                         gaPageTable[Addr>>12] |= PF_PRESENT|PF_WRITE;
72                 }
73                 else
74                 {
75                         //Log("MM_PageFault: COW - MM_DuplicatePage(0x%x)", Addr);
76                         paddr = MM_DuplicatePage( Addr );
77                         MM_DerefPhys( gaPageTable[Addr>>12] & ~0xFFF );
78                         gaPageTable[Addr>>12] &= PF_USER;
79                         gaPageTable[Addr>>12] |= paddr|PF_PRESENT|PF_WRITE;
80                 }
81                 
82                 INVLPG( Addr & ~0xFFF );
83                 return;
84         }
85         #endif
86         
87         // If it was a user, tell the thread handler
88         if(ErrorCode & 4) {
89                 Warning("%s %s %s memory%s",
90                         (ErrorCode&4?"User":"Kernel"),
91                         (ErrorCode&2?"write to":"read from"),
92                         (ErrorCode&1?"bad/locked":"non-present"),
93                         (ErrorCode&16?" (Instruction Fetch)":"")
94                         );
95                 Warning("User Pagefault: Instruction at %04x:%08x accessed %p",
96                         Regs->CS, Regs->RIP, Addr);
97                 __asm__ __volatile__ ("sti");   // Restart IRQs
98 //              Threads_SegFault(Addr);
99                 return ;
100         }
101         
102         // Kernel #PF
103         Debug_KernelPanic();
104         // -- Check Error Code --
105         if(ErrorCode & 8)
106                 Warning("Reserved Bits Trashed!");
107         else
108         {
109                 Warning("%s %s %s memory%s",
110                         (ErrorCode&4?"User":"Kernel"),
111                         (ErrorCode&2?"write to":"read from"),
112                         (ErrorCode&1?"bad/locked":"non-present"),
113                         (ErrorCode&16?" (Instruction Fetch)":"")
114                         );
115         }
116         
117         Log("Code at %p accessed %p", Regs->RIP, Addr);
118         // Print Stack Backtrace
119 //      Error_Backtrace(Regs->RIP, Regs->RBP);
120         
121         MM_DumpTables(0, -1);
122         
123         __asm__ __volatile__ ("cli");
124         for( ;; )
125                 HALT();
126 }
127
128 /**
129  * \brief Dumps the layout of the page tables
130  */
131 void MM_DumpTables(tVAddr Start, tVAddr End)
132 {
133         const tPAddr    CHANGEABLE_BITS = 0xFF8;
134         const tPAddr    MASK = ~CHANGEABLE_BITS;        // Physical address and access bits
135         tVAddr  rangeStart = 0;
136         tPAddr  expected = CHANGEABLE_BITS;     // MASK is used because it's not a vaild value
137         tVAddr  curPos;
138         Uint    page;
139         
140         End &= (1L << 48) - 1;
141         
142         Start >>= 12;   End >>= 12;
143         
144         Log("Table Entries:");
145         for(page = Start, curPos = Start<<12;
146                 page < End;
147                 curPos += 0x1000, page++)
148         {
149                 if( curPos == 0x800000000000L )
150                         curPos = 0xFFFF800000000000L;
151                 
152                 // End of a range
153                 if(
154                         !(PAGEMAPLVL4(page>>27) & PF_PRESENT)
155                 ||      !(PAGEDIRPTR(page>>18) & PF_PRESENT)
156                 ||      !(PAGEDIR(page>>9) & PF_PRESENT)
157                 ||  !(PAGETABLE(page) & PF_PRESENT)
158                 ||  (PAGETABLE(page) & MASK) != expected)
159                 {                       
160                         if(expected != CHANGEABLE_BITS) {
161                                 Log("%016x-0x%016x => %013x-%013x (%c%c%c%c)",
162                                         rangeStart, curPos - 1,
163                                         PAGETABLE(rangeStart>>12) & ~0xFFF,
164                                         (expected & ~0xFFF) - 1,
165                                         (expected & PF_PAGED ? 'p' : '-'),
166                                         (expected & PF_COW ? 'C' : '-'),
167                                         (expected & PF_USER ? 'U' : '-'),
168                                         (expected & PF_WRITE ? 'W' : '-')
169                                         );
170                                 expected = CHANGEABLE_BITS;
171                         }
172                         if( !(PAGEMAPLVL4(page>>27) & PF_PRESENT) ) {
173                                 page += (1 << 27) - 1;
174                                 curPos += (1L << 39) - 0x1000;
175                                 continue;
176                         }
177                         if( !(PAGEDIRPTR(page>>18) & PF_PRESENT) ) {
178                                 page += (1 << 18) - 1;
179                                 curPos += (1L << 30) - 0x1000;
180                                 continue;
181                         }
182                         if( !(PAGEDIR(page>>9) & PF_PRESENT) ) {
183                                 page += (1 << 9) - 1;
184                                 curPos += (1L << 21) - 0x1000;
185                                 continue;
186                         }
187                         if( !(PAGETABLE(page) & PF_PRESENT) )   continue;
188                         
189                         expected = (PAGETABLE(page) & MASK);
190                         rangeStart = curPos;
191                 }
192                 if(expected != CHANGEABLE_BITS)
193                         expected += 0x1000;
194         }
195         
196         if(expected != CHANGEABLE_BITS) {
197                 Log("%016x-%016x => %013x-%013x (%s%s%s%s)",
198                         rangeStart, curPos - 1,
199                         PAGETABLE(rangeStart>>12) & ~0xFFF,
200                         (expected & ~0xFFF) - 1,
201                         (expected & PF_PAGED ? "p" : "-"),
202                         (expected & PF_COW ? "C" : "-"),
203                         (expected & PF_USER ? "U" : "-"),
204                         (expected & PF_WRITE ? "W" : "-")
205                         );
206                 expected = 0;
207         }
208 }
209
210 /**
211  * \brief Map a physical page to a virtual one
212  */
213 int MM_Map(tVAddr VAddr, tPAddr PAddr)
214 {
215         tPAddr  tmp;
216         
217         ENTER("xVAddr xPAddr", VAddr, PAddr);
218         
219         // Check PML4
220         //Log(" MM_Map: &PAGEMAPLVL4(%x) = %x", VAddr >> 39, &PAGEMAPLVL4(VAddr >> 39));
221         //Log(" MM_Map: &PAGEDIRPTR(%x) = %x", VAddr >> 30, &PAGEDIRPTR(VAddr >> 30));
222         //Log(" MM_Map: &PAGEDIR(%x) = %x", VAddr >> 21, &PAGEDIR(VAddr >> 21));
223         //Log(" MM_Map: &PAGETABLE(%x) = %x", VAddr >> 12, &PAGETABLE(VAddr >> 12));
224         //Log(" MM_Map: &PAGETABLE(0) = %x", &PAGETABLE(0));
225         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
226         {
227                 tmp = MM_AllocPhys();
228                 if(!tmp)        return 0;
229                 PAGEMAPLVL4(VAddr >> 39) = tmp | 3;
230                 INVLPG( &PAGEDIRPTR( (VAddr>>39)<<9 ) );
231                 memset( &PAGEDIRPTR( (VAddr>>39)<<9 ), 0, 4096 );
232         }
233         
234         // Check PDP
235         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
236         {
237                 tmp = MM_AllocPhys();
238                 if(!tmp)        return 0;
239                 PAGEDIRPTR(VAddr >> 30) = tmp | 3;
240                 INVLPG( &PAGEDIR( (VAddr>>30)<<9 ) );
241                 memset( &PAGEDIR( (VAddr>>30)<<9 ), 0, 0x1000 );
242         }
243         
244         // Check Page Dir
245         if( !(PAGEDIR(VAddr >> 21) & 1) )
246         {
247                 tmp = MM_AllocPhys();
248                 if(!tmp)        return 0;
249                 PAGEDIR(VAddr >> 21) = tmp | 3;
250                 INVLPG( &PAGETABLE( (VAddr>>21)<<9 ) );
251                 memset( &PAGETABLE( (VAddr>>21)<<9 ), 0, 4096 );
252         }
253         
254         // Check if this virtual address is already mapped
255         if( PAGETABLE(VAddr >> PTAB_SHIFT) & 1 )
256                 return 0;
257         
258         PAGETABLE(VAddr >> PTAB_SHIFT) = PAddr | 3;
259         
260         INVLPG( VAddr );
261
262         LEAVE('i', 1);  
263         return 1;
264 }
265
266 /**
267  * \brief Removed a mapped page
268  */
269 void MM_Unmap(tVAddr VAddr)
270 {
271         // Check PML4
272         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )   return ;
273         // Check PDP
274         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )    return ;
275         // Check Page Dir
276         if( !(PAGEDIR(VAddr >> 21) & 1) )       return ;
277         
278         PAGETABLE(VAddr >> PTAB_SHIFT) = 0;
279         INVLPG( VAddr );
280 }
281
282 /**
283  * \brief Allocate a block of memory at the specified virtual address
284  */
285 tPAddr MM_Allocate(tVAddr VAddr)
286 {
287         tPAddr  ret;
288         
289         ENTER("xVAddr", VAddr);
290         
291         // NOTE: This is hack, but I like my dumps to be neat
292         #if 1
293         if( !MM_Map(VAddr, 0) ) // Make sure things are allocated
294         {
295                 Warning("MM_Allocate: Unable to map, tables did not initialise");
296                 LEAVE('i', 0);
297                 return 0;
298         }
299         MM_Unmap(VAddr);
300         #endif
301         
302         ret = MM_AllocPhys();
303         LOG("ret = %x", ret);
304         if(!ret) {
305                 LEAVE('i', 0);
306                 return 0;
307         }
308         
309         if( !MM_Map(VAddr, ret) )
310         {
311                 Warning("MM_Allocate: Unable to map. Strange, we should have errored earlier");
312                 MM_DerefPhys(ret);
313                 LEAVE('i');
314                 return 0;
315         }
316         
317         LEAVE('x', ret);
318         return ret;
319 }
320
321 void MM_Deallocate(tVAddr VAddr)
322 {
323         tPAddr  phys;
324         
325         phys = MM_GetPhysAddr(VAddr);
326         if(!phys)       return ;
327         
328         MM_Unmap(VAddr);
329         
330         MM_DerefPhys(phys);
331 }
332
333 /**
334  * \brief Get the physical address of a virtual location
335  */
336 tPAddr MM_GetPhysAddr(tVAddr Addr)
337 {
338         Log("MM_GetPhysAddr: (Addr=0x%x)", Addr);
339         if( !(PAGEMAPLVL4(Addr >> 39) & 1) )
340                 return 0;
341         Log(" MM_GetPhysAddr: PDP Valid");
342         if( !(PAGEDIRPTR(Addr >> 30) & 1) )
343                 return 0;
344         Log(" MM_GetPhysAddr: PD Valid");
345         if( !(PAGEDIR(Addr >> 21) & 1) )
346                 return 0;
347         Log(" MM_GetPhysAddr: PT Valid");
348         if( !(PAGETABLE(Addr >> PTAB_SHIFT) & 1) )
349                 return 0;
350         Log(" MM_GetPhysAddr: Page Valid");
351         
352         return (PAGETABLE(Addr >> PTAB_SHIFT) & ~0xFFF) | (Addr & 0xFFF);
353 }
354
355 /**
356  * \brief Sets the flags on a page
357  */
358 void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
359 {
360         tPAddr  *ent;
361         
362         // Validity Check
363         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
364                 return ;
365         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
366                 return ;
367         if( !(PAGEDIR(VAddr >> 21) & 1) )
368                 return ;
369         if( !(PAGETABLE(VAddr >> 12) & 1) )
370                 return ;
371         
372         // Set Flags
373         ent = &PAGETABLE(VAddr >> 12);
374         
375         // Read-Only
376         if( Mask & MM_PFLAG_RO )
377         {
378                 if( Flags & MM_PFLAG_RO ) {
379                         *ent &= ~PF_WRITE;
380                 }
381                 else {
382                         *ent |= PF_WRITE;
383                 }
384         }
385         
386         // Kernel
387         if( Mask & MM_PFLAG_KERNEL )
388         {
389                 if( Flags & MM_PFLAG_KERNEL ) {
390                         *ent &= ~PF_USER;
391                 }
392                 else {
393                         *ent |= PF_USER;
394                 }
395         }
396         
397         // Copy-On-Write
398         if( Mask & MM_PFLAG_COW )
399         {
400                 if( Flags & MM_PFLAG_COW ) {
401                         *ent &= ~PF_WRITE;
402                         *ent |= PF_COW;
403                 }
404                 else {
405                         *ent &= ~PF_COW;
406                         *ent |= PF_WRITE;
407                 }
408         }
409         
410         // Execute
411         if( Mask & MM_PFLAG_EXEC )
412         {
413                 if( Flags & MM_PFLAG_EXEC ) {
414                         *ent &= ~PF_NX;
415                 }
416                 else {
417                         *ent |= PF_NX;
418                 }
419         }
420 }
421
422 /**
423  * \brief Get the flags applied to a page
424  */
425 Uint MM_GetFlags(tVAddr VAddr)
426 {
427         tPAddr  *ent;
428         Uint    ret = 0;
429         
430         // Validity Check
431         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
432                 return 0;
433         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
434                 return 0;
435         if( !(PAGEDIR(VAddr >> 21) & 1) )
436                 return 0;
437         if( !(PAGETABLE(VAddr >> 12) & 1) )
438                 return 0;
439         
440         // Set Flags
441         ent = &PAGETABLE(VAddr >> 12);
442         
443         // Read-Only
444         if( !(*ent & PF_WRITE) )        ret |= MM_PFLAG_RO;
445         // Kernel
446         if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL;
447         // Copy-On-Write
448         if( *ent & PF_COW )     ret |= MM_PFLAG_COW;    
449         // Execute
450         if( !(*ent & PF_NX) )   ret |= MM_PFLAG_EXEC;
451         
452         return ret;
453 }
454
455 // --- Hardware Mappings ---
456 /**
457  * \brief Map a range of hardware pages
458  */
459 tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
460 {
461         Log_KernelPanic("MM", "TODO: Implement MM_MapHWPages");
462         return 0;
463 }
464
465 /**
466  * \brief Free a range of hardware pages
467  */
468 void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
469 {
470         Log_KernelPanic("MM", "TODO: Implement MM_UnmapHWPages");
471 }
472
473 // --- Tempory Mappings ---
474 tVAddr MM_MapTemp(tPAddr PAddr)
475 {
476         Log_KernelPanic("MM", "TODO: Implement MM_MapTemp");
477         return 0;
478 }
479
480 void MM_FreeTemp(tVAddr VAddr)
481 {
482         Log_KernelPanic("MM", "TODO: Implement MM_FreeTemp");
483         return ;
484 }
485
486
487 // --- Address Space Clone --
488 tPAddr MM_Clone(void)
489 {
490         tPAddr  ret;
491         
492         // #1 Create a copy of the PML4
493         ret = MM_AllocPhys();
494         if(!ret)        return 0;
495         
496         Log_KernelPanic("MM", "TODO: Implement MM_Clone");
497         
498         // #2 Alter the fractal pointer
499         // #3 Set Copy-On-Write to all user pages
500         // #4 Return
501         return 0;
502 }
503
504 void MM_ClearUser(void)
505 {
506         tVAddr  addr = 0;
507         // #1 Traverse the structure < 2^47, Deref'ing all pages
508         // #2 Free tables/dirs/pdps once they have been cleared
509         
510         for( addr = 0; addr < 0x800000000000; )
511         {
512                 if( PAGEMAPLVL4(addr >> PML4_SHIFT) & 1 )
513                 {
514                         if( PAGEDIRPTR(addr >> PDP_SHIFT) & 1 )
515                         {
516                                 if( PAGEDIR(addr >> PDIR_SHIFT) & 1 )
517                                 {
518                                         // Page
519                                         if( PAGETABLE(addr >> PTAB_SHIFT) & 1 ) {
520                                                 MM_DerefPhys( PAGETABLE(addr >> PTAB_SHIFT) & PADDR_MASK );
521                                                 PAGETABLE(addr >> PTAB_SHIFT) = 0;
522                                         }
523                                         addr += 1 << PTAB_SHIFT;
524                                         // Dereference the PDIR Entry
525                                         if( (addr + (1 << PTAB_SHIFT)) >> PDIR_SHIFT != (addr >> PDIR_SHIFT) ) {
526                                                 MM_DerefPhys( PAGEMAPLVL4(addr >> PDIR_SHIFT) & PADDR_MASK );
527                                                 PAGEDIR(addr >> PDIR_SHIFT) = 0;
528                                         }
529                                 }
530                                 else {
531                                         addr += 1 << PDIR_SHIFT;
532                                         continue;
533                                 }
534                                 // Dereference the PDP Entry
535                                 if( (addr + (1 << PDIR_SHIFT)) >> PDP_SHIFT != (addr >> PDP_SHIFT) ) {
536                                         MM_DerefPhys( PAGEMAPLVL4(addr >> PDP_SHIFT) & PADDR_MASK );
537                                         PAGEDIRPTR(addr >> PDP_SHIFT) = 0;
538                                 }
539                         }
540                         else {
541                                 addr += 1 << PDP_SHIFT;
542                                 continue;
543                         }
544                         // Dereference the PML4 Entry
545                         if( (addr + (1 << PDP_SHIFT)) >> PML4_SHIFT != (addr >> PML4_SHIFT) ) {
546                                 MM_DerefPhys( PAGEMAPLVL4(addr >> PML4_SHIFT) & PADDR_MASK );
547                                 PAGEMAPLVL4(addr >> PML4_SHIFT) = 0;
548                         }
549                 }
550                 else {
551                         addr += (tVAddr)1 << PML4_SHIFT;
552                         continue;
553                 }
554         }
555 }
556
557 tVAddr MM_NewWorkerStack(void)
558 {
559         Log_KernelPanic("MM", "TODO: Implement MM_NewWorkerStack");
560         return 0;
561 }
562
563 /**
564  * \brief Allocate a new kernel stack
565  */
566 tVAddr MM_NewKStack(void)
567 {
568         tVAddr  base = MM_KSTACK_BASE;
569         Uint    i;
570         for( ; base < MM_KSTACK_TOP; base += KERNEL_STACK_SIZE )
571         {
572                 if(MM_GetPhysAddr(base) != 0)
573                         continue;
574                 
575                 Log("MM_NewKStack: Found one at %p", base + KERNEL_STACK_SIZE);
576                 for( i = 0; i < KERNEL_STACK_SIZE; i += 0x1000)
577                         MM_Allocate(base+i);
578                 
579                 return base + KERNEL_STACK_SIZE;
580         }
581         Log_Warning("MM", "MM_NewKStack - No address space left\n");
582         return 0;
583 }

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