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

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