Bugfixing
[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         if( !(PAGEMAPLVL4(Addr >> 39) & 1) )
339                 return 0;
340         if( !(PAGEDIRPTR(Addr >> 30) & 1) )
341                 return 0;
342         if( !(PAGEDIR(Addr >> 21) & 1) )
343                 return 0;
344         if( !(PAGETABLE(Addr >> PTAB_SHIFT) & 1) )
345                 return 0;
346         
347         return (PAGETABLE(Addr >> PTAB_SHIFT) & ~0xFFF) | (Addr & 0xFFF);
348 }
349
350 /**
351  * \brief Sets the flags on a page
352  */
353 void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
354 {
355         tPAddr  *ent;
356         
357         // Validity Check
358         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
359                 return ;
360         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
361                 return ;
362         if( !(PAGEDIR(VAddr >> 21) & 1) )
363                 return ;
364         if( !(PAGETABLE(VAddr >> 12) & 1) )
365                 return ;
366         
367         // Set Flags
368         ent = &PAGETABLE(VAddr >> 12);
369         
370         // Read-Only
371         if( Mask & MM_PFLAG_RO )
372         {
373                 if( Flags & MM_PFLAG_RO ) {
374                         *ent &= ~PF_WRITE;
375                 }
376                 else {
377                         *ent |= PF_WRITE;
378                 }
379         }
380         
381         // Kernel
382         if( Mask & MM_PFLAG_KERNEL )
383         {
384                 if( Flags & MM_PFLAG_KERNEL ) {
385                         *ent &= ~PF_USER;
386                 }
387                 else {
388                         *ent |= PF_USER;
389                 }
390         }
391         
392         // Copy-On-Write
393         if( Mask & MM_PFLAG_COW )
394         {
395                 if( Flags & MM_PFLAG_COW ) {
396                         *ent &= ~PF_WRITE;
397                         *ent |= PF_COW;
398                 }
399                 else {
400                         *ent &= ~PF_COW;
401                         *ent |= PF_WRITE;
402                 }
403         }
404         
405         // Execute
406         if( Mask & MM_PFLAG_EXEC )
407         {
408                 if( Flags & MM_PFLAG_EXEC ) {
409                         *ent &= ~PF_NX;
410                 }
411                 else {
412                         *ent |= PF_NX;
413                 }
414         }
415 }
416
417 /**
418  * \brief Get the flags applied to a page
419  */
420 Uint MM_GetFlags(tVAddr VAddr)
421 {
422         tPAddr  *ent;
423         Uint    ret = 0;
424         
425         // Validity Check
426         if( !(PAGEMAPLVL4(VAddr >> 39) & 1) )
427                 return 0;
428         if( !(PAGEDIRPTR(VAddr >> 30) & 1) )
429                 return 0;
430         if( !(PAGEDIR(VAddr >> 21) & 1) )
431                 return 0;
432         if( !(PAGETABLE(VAddr >> 12) & 1) )
433                 return 0;
434         
435         // Set Flags
436         ent = &PAGETABLE(VAddr >> 12);
437         
438         // Read-Only
439         if( !(*ent & PF_WRITE) )        ret |= MM_PFLAG_RO;
440         // Kernel
441         if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL;
442         // Copy-On-Write
443         if( *ent & PF_COW )     ret |= MM_PFLAG_COW;    
444         // Execute
445         if( !(*ent & PF_NX) )   ret |= MM_PFLAG_EXEC;
446         
447         return ret;
448 }
449
450 // --- Hardware Mappings ---
451 /**
452  * \brief Map a range of hardware pages
453  */
454 tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
455 {
456         Log_KernelPanic("MM", "TODO: Implement MM_MapHWPages");
457         return 0;
458 }
459
460 /**
461  * \brief Free a range of hardware pages
462  */
463 void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
464 {
465         Log_KernelPanic("MM", "TODO: Implement MM_UnmapHWPages");
466 }
467
468 // --- Tempory Mappings ---
469 tVAddr MM_MapTemp(tPAddr PAddr)
470 {
471         Log_KernelPanic("MM", "TODO: Implement MM_MapTemp");
472         return 0;
473 }
474
475 void MM_FreeTemp(tVAddr VAddr)
476 {
477         Log_KernelPanic("MM", "TODO: Implement MM_FreeTemp");
478         return ;
479 }
480
481
482 // --- Address Space Clone --
483 tPAddr MM_Clone(void)
484 {
485         tPAddr  ret;
486         
487         // #1 Create a copy of the PML4
488         ret = MM_AllocPhys();
489         if(!ret)        return 0;
490         
491         Log_KernelPanic("MM", "TODO: Implement MM_Clone");
492         
493         // #2 Alter the fractal pointer
494         // #3 Set Copy-On-Write to all user pages
495         // #4 Return
496         return 0;
497 }
498
499 void MM_ClearUser(void)
500 {
501         tVAddr  addr = 0;
502         // #1 Traverse the structure < 2^47, Deref'ing all pages
503         // #2 Free tables/dirs/pdps once they have been cleared
504         
505         for( addr = 0; addr < 0x800000000000; )
506         {
507                 if( PAGEMAPLVL4(addr >> PML4_SHIFT) & 1 )
508                 {
509                         if( PAGEDIRPTR(addr >> PDP_SHIFT) & 1 )
510                         {
511                                 if( PAGEDIR(addr >> PDIR_SHIFT) & 1 )
512                                 {
513                                         // Page
514                                         if( PAGETABLE(addr >> PTAB_SHIFT) & 1 ) {
515                                                 MM_DerefPhys( PAGETABLE(addr >> PTAB_SHIFT) & PADDR_MASK );
516                                                 PAGETABLE(addr >> PTAB_SHIFT) = 0;
517                                         }
518                                         addr += 1 << PTAB_SHIFT;
519                                         // Dereference the PDIR Entry
520                                         if( (addr + (1 << PTAB_SHIFT)) >> PDIR_SHIFT != (addr >> PDIR_SHIFT) ) {
521                                                 MM_DerefPhys( PAGEMAPLVL4(addr >> PDIR_SHIFT) & PADDR_MASK );
522                                                 PAGEDIR(addr >> PDIR_SHIFT) = 0;
523                                         }
524                                 }
525                                 else {
526                                         addr += 1 << PDIR_SHIFT;
527                                         continue;
528                                 }
529                                 // Dereference the PDP Entry
530                                 if( (addr + (1 << PDIR_SHIFT)) >> PDP_SHIFT != (addr >> PDP_SHIFT) ) {
531                                         MM_DerefPhys( PAGEMAPLVL4(addr >> PDP_SHIFT) & PADDR_MASK );
532                                         PAGEDIRPTR(addr >> PDP_SHIFT) = 0;
533                                 }
534                         }
535                         else {
536                                 addr += 1 << PDP_SHIFT;
537                                 continue;
538                         }
539                         // Dereference the PML4 Entry
540                         if( (addr + (1 << PDP_SHIFT)) >> PML4_SHIFT != (addr >> PML4_SHIFT) ) {
541                                 MM_DerefPhys( PAGEMAPLVL4(addr >> PML4_SHIFT) & PADDR_MASK );
542                                 PAGEMAPLVL4(addr >> PML4_SHIFT) = 0;
543                         }
544                 }
545                 else {
546                         addr += (tVAddr)1 << PML4_SHIFT;
547                         continue;
548                 }
549         }
550 }
551
552 tVAddr MM_NewWorkerStack(void)
553 {
554         Log_KernelPanic("MM", "TODO: Implement MM_NewWorkerStack");
555         return 0;
556 }
557
558 /**
559  * \brief Allocate a new kernel stack
560  */
561 tVAddr MM_NewKStack(void)
562 {
563         tVAddr  base = MM_KSTACK_BASE;
564         Uint    i;
565         for( ; base < MM_KSTACK_TOP; base += KERNEL_STACK_SIZE )
566         {
567                 if(MM_GetPhysAddr(base) != 0)
568                         continue;
569                 
570                 Log("MM_NewKStack: Found one at %p", base + KERNEL_STACK_SIZE);
571                 for( i = 0; i < KERNEL_STACK_SIZE; i += 0x1000)
572                         MM_Allocate(base+i);
573                 
574                 return base + KERNEL_STACK_SIZE;
575         }
576         Log_Warning("MM", "MM_NewKStack - No address space left\n");
577         return 0;
578 }

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