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

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