Merge branch 'master' of git://ted.mutabah.net/acess2
[tpg/acess2.git] / KernelLand / Kernel / arch / x86 / mm_virt.c
1 /*
2  * AcessOS Microkernel Version
3  * mm_virt.c
4  * 
5  * Memory Map
6  * 0xE0 - Kernel Base
7  * 0xF0 - Kernel Stacks
8  * 0xFD - Fractals
9  * 0xFE - Unused
10  * 0xFF - System Calls / Kernel's User Code
11  */
12 #define DEBUG   0
13 #define SANITY  1
14 #include <acess.h>
15 #include <mm_virt.h>
16 #include <mm_phys.h>
17 #include <proc.h>
18 #include <hal_proc.h>
19 #include <arch_int.h>
20 #include <semaphore.h>
21
22 #include "include/vmem_layout.h"
23
24 #define TRACE_MAPS      0
25
26 #define TAB     22
27
28 #define PF_PRESENT      0x1
29 #define PF_WRITE        0x2
30 #define PF_USER         0x4
31 #define PF_GLOBAL       0x80
32 #define PF_COW          0x200
33 #define PF_NOPAGE       0x400
34
35 #define INVLPG(addr)    __asm__ __volatile__ ("invlpg (%0)"::"r"(addr))
36
37 #define GET_TEMP_MAPPING(cr3) do { \
38         __ASM__("cli"); \
39         __AtomicTestSetLoop( (Uint *)gpTmpCR3, cr3 | 3 ); \
40 } while(0)
41 #define REL_TEMP_MAPPING() do { \
42         *gpTmpCR3 = 0; \
43         __ASM__("sti"); \
44 } while(0)
45
46 typedef Uint32  tTabEnt;
47
48 // === IMPORTS ===
49 extern tPage    _UsertextEnd;
50 extern tPage    _UsertextBase;
51 extern tPage    gKernelEnd;     // defined as page aligned
52 extern Uint32   gaInitPageDir[1024];
53 extern Uint32   gaInitPageTable[1024];
54 extern void     Threads_SegFault(tVAddr Addr);
55
56 // === PROTOTYPES ===
57 void    MM_PreinitVirtual(void);
58 void    MM_InstallVirtual(void);
59 void    MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
60 void    MM_DumpTables_Print(tVAddr Start, Uint32 Orig, size_t Size, void *Node);
61 //void  MM_DumpTables(tVAddr Start, tVAddr End);
62 //void  MM_ClearUser(void);
63 tPAddr  MM_DuplicatePage(tVAddr VAddr);
64
65 // === GLOBALS ===
66 #define gaPageTable     ((tTabEnt*)PAGE_TABLE_ADDR)
67 #define gaPageDir       ((tTabEnt*)PAGE_DIR_ADDR)
68 #define gaTmpTable      ((tTabEnt*)TMP_TABLE_ADDR)
69 #define gaTmpDir        ((tTabEnt*)TMP_DIR_ADDR)
70 #define gpPageCR3       ((tTabEnt*)PAGE_CR3_ADDR)
71 #define gpTmpCR3        ((tTabEnt*)TMP_CR3_ADDR)
72
73 #define gaPAE_PageTable ((tTabEnt*)PAE_PAGE_TABLE_ADDR)
74 #define gaPAE_PageDir   ((tTabEnt*)PAE_PAGE_DIR_ADDR)
75 #define gaPAE_MainPDPT  ((tTabEnt*)PAE_PAGE_PDPT_ADDR)
76 #define gaPAE_TmpTable  ((tTabEnt*)PAE_TMP_DIR_ADDR)
77 #define gaPAE_TmpDir    ((tTabEnt*)PAE_TMP_DIR_ADDR)
78 #define gaPAE_TmpPDPT   ((tTabEnt*)PAE_TMP_PDPT_ADDR)
79  int    gbUsePAE = 0;
80 tMutex  glTempMappings;
81 tSemaphore      gTempMappingsSem;
82 tMutex  glTempFractal;
83 Uint32  gWorkerStacks[(NUM_WORKER_STACKS+31)/32];
84  int    giLastUsedWorker = 0;
85 struct sPageInfo {
86         void    *Node;
87         tVAddr  Base;
88         Uint64  Offset;
89          int    Length;
90          int    Flags;
91 }       *gaMappedRegions;       // sizeof = 24 bytes
92 // - Zero page
93 tShortSpinlock  glMM_ZeroPage;
94 tPAddr  giMM_ZeroPage;
95
96 // === CODE ===
97 /**
98  * \fn void MM_PreinitVirtual(void)
99  * \brief Maps the fractal mappings
100  */
101 void MM_PreinitVirtual(void)
102 {
103         gaInitPageDir[ PAGE_TABLE_ADDR >> 22 ] = ((tTabEnt)&gaInitPageDir - KERNEL_BASE) | 3;
104         INVLPG( PAGE_TABLE_ADDR );
105         
106         Semaphore_Init(&gTempMappingsSem, NUM_TEMP_PAGES, NUM_TEMP_PAGES, "MMVirt", "Temp Mappings");
107 }
108
109 /**
110  * \fn void MM_InstallVirtual(void)
111  * \brief Sets up the constant page mappings
112  */
113 void MM_InstallVirtual(void)
114 {
115         // Don't bother referencing, as it'a in the kernel area
116         //MM_RefPhys( gaInitPageDir[ PAGE_TABLE_ADDR >> 22 ] );
117         // --- Pre-Allocate kernel tables
118         for( int i = KERNEL_BASE>>22; i < 1024; i ++ )
119         {
120                 if( gaPageDir[ i ] ) {
121                 //      MM_RefPhys( gaPageDir[ i ] & ~0xFFF );
122                         continue;
123                 }       
124                 // Skip stack tables, they are process unique
125                 if( i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) {
126                         gaPageDir[ i ] = 0;
127                         continue;
128                 }
129                 // Preallocate table
130                 gaPageDir[ i ] = MM_AllocPhys() | 3;
131                 INVLPG( &gaPageTable[i*1024] );
132                 memset( &gaPageTable[i*1024], 0, 0x1000 );
133         }
134         
135         // Unset kernel on the User Text pages
136         ASSERT( ((tVAddr)&_UsertextBase & (PAGE_SIZE-1)) == 0 );
137         //ASSERT( ((tVAddr)&_UsertextEnd & (PAGE_SIZE-1)) == 0 );
138         for( tPage *page = &_UsertextBase; page < &_UsertextEnd; page ++ )
139         {
140                 MM_SetFlags( page, 0, MM_PFLAG_KERNEL );
141         }
142
143         // Unmap the area between end of kernel image and the heap
144         // DISABLED: Assumptions in main.c
145         #if 0
146         for( tPage *page = &gKernelEnd; page < (tPage*)(KERNEL_BASE+4*1024*1024); page ++ )
147         {
148                 gaPageTable[ (tVAddr)page / PAGE_SIZE ] = 0;
149                 //MM_Deallocate(page);
150         }
151         #endif
152
153         *gpTmpCR3 = 0;
154 }
155
156 /**
157  * \brief Cleans up the SMP required mappings
158  */
159 void MM_FinishVirtualInit(void)
160 {
161         gaInitPageDir[ 0 ] = 0;
162 }
163
164 /**
165  * \fn void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
166  * \brief Called on a page fault
167  */
168 void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
169 {
170         //ENTER("xAddr bErrorCode", Addr, ErrorCode);
171         
172         // -- Check for COW --
173         if( gaPageDir  [Addr>>22] & PF_PRESENT  && gaPageTable[Addr>>12] & PF_PRESENT
174          && gaPageTable[Addr>>12] & PF_COW )
175         {
176                 tPAddr  paddr;
177                 __asm__ __volatile__ ("sti");
178                 if(MM_GetRefCount( gaPageTable[Addr>>12] & ~0xFFF ) == 1)
179                 {
180                         gaPageTable[Addr>>12] &= ~PF_COW;
181                         gaPageTable[Addr>>12] |= PF_PRESENT|PF_WRITE;
182                 }
183                 else
184                 {
185                         //Log("MM_PageFault: COW - MM_DuplicatePage(0x%x)", Addr);
186                         paddr = MM_DuplicatePage( Addr );
187                         MM_DerefPhys( gaPageTable[Addr>>12] & ~0xFFF );
188                         gaPageTable[Addr>>12] &= PF_USER;
189                         gaPageTable[Addr>>12] |= paddr|PF_PRESENT|PF_WRITE;
190                 }
191                 
192 //              Log_Debug("MMVirt", "COW for %p (%P)", Addr, gaPageTable[Addr>>12]);
193                 
194                 INVLPG( Addr & ~0xFFF );
195                 return;
196         }
197
198         // Disable instruction tracing  
199         __ASM__("pushf; andw $0xFEFF, 0(%esp); popf");
200         Proc_GetCurThread()->bInstrTrace = 0;
201
202         // If it was a user, tell the thread handler
203         if(ErrorCode & 4) {
204                 __asm__ __volatile__ ("sti");
205                 Log_Warning("MMVirt", "User %s %s memory%s",
206                         (ErrorCode&2?"write to":"read from"),
207                         (ErrorCode&1?"bad/locked":"non-present"),
208                         (ErrorCode&16?" (Instruction Fetch)":"")
209                         );
210                 Log_Warning("MMVirt", "Instruction %04x:%08x accessed %p", Regs->cs, Regs->eip, Addr);
211                 __ASM__("sti"); // Restart IRQs
212                 #if 1
213                 Error_Backtrace(Regs->eip, Regs->ebp);
214                 #endif
215                 Threads_SegFault(Addr);
216                 return ;
217         }
218         
219         Debug_KernelPanic();
220         
221         // -- Check Error Code --
222         if(ErrorCode & 8)
223                 Warning("Reserved Bits Trashed!");
224         else
225         {
226                 Warning("Kernel %s %s memory%s",
227                         (ErrorCode&2?"write to":"read from"),
228                         (ErrorCode&1?"bad/locked":"non-present"),
229                         (ErrorCode&16?" (Instruction Fetch)":"")
230                         );
231         }
232         
233         Log("CPU %i - Code at %p accessed %p", GetCPUNum(), Regs->eip, Addr);
234         // Print Stack Backtrace
235         Error_Backtrace(Regs->eip, Regs->ebp);
236
237         #if 0   
238         Log("gaPageDir[0x%x] = 0x%x", Addr>>22, gaPageDir[Addr>>22]);
239         if( gaPageDir[Addr>>22] & PF_PRESENT )
240                 Log("gaPageTable[0x%x] = 0x%x", Addr>>12, gaPageTable[Addr>>12]);
241         #endif
242         //MM_DumpTables(0, -1); 
243         
244         // Register Dump
245         Log("EAX %08x ECX %08x EDX %08x EBX %08x", Regs->eax, Regs->ecx, Regs->edx, Regs->ebx);
246         Log("ESP %08x EBP %08x ESI %08x EDI %08x", Regs->esp, Regs->ebp, Regs->esi, Regs->edi);
247         //Log("SS:ESP %04x:%08x", Regs->ss, Regs->esp);
248         Log("CS:EIP %04x:%08x", Regs->cs, Regs->eip);
249         Log("DS %04x ES %04x FS %04x GS %04x", Regs->ds, Regs->es, Regs->fs, Regs->gs);
250         {
251                 Uint    dr0, dr1;
252                 __ASM__ ("mov %%dr0, %0":"=r"(dr0):);
253                 __ASM__ ("mov %%dr1, %0":"=r"(dr1):);
254                 Log("DR0 %08x DR1 %08x", dr0, dr1);
255         }
256         
257         Panic("Page Fault at 0x%x (Accessed 0x%x)", Regs->eip, Addr);
258 }
259
260 void MM_DumpTables_Print(tVAddr Start, Uint32 Orig, size_t Size, void *Node)
261 {
262         if( (Orig & ~(PAGE_SIZE-1)) == giMM_ZeroPage )
263         {
264                 Log( "0x%08x => ZERO + 0x%08x (%s%s%s%s%s) %p",
265                         Start,
266                         Size,
267                         (Orig & PF_NOPAGE ? "P" : "-"),
268                         (Orig & PF_COW ? "C" : "-"),
269                         (Orig & PF_GLOBAL ? "G" : "-"),
270                         (Orig & PF_USER ? "U" : "-"),
271                         (Orig & PF_WRITE ? "W" : "-"),
272                         Node
273                         );
274         }
275         else
276         {
277                 Log(" 0x%08x => 0x%08x + 0x%08x (%s%s%s%s%s) %p",
278                         Start,
279                         Orig & ~0xFFF,
280                         Size,
281                         (Orig & PF_NOPAGE ? "P" : "-"),
282                         (Orig & PF_COW ? "C" : "-"),
283                         (Orig & PF_GLOBAL ? "G" : "-"),
284                         (Orig & PF_USER ? "U" : "-"),
285                         (Orig & PF_WRITE ? "W" : "-"),
286                         Node
287                         );
288         }
289 }
290
291 /**
292  * \fn void MM_DumpTables(tVAddr Start, tVAddr End)
293  * \brief Dumps the layout of the page tables
294  */
295 void MM_DumpTables(tVAddr Start, tVAddr End)
296 {
297         tVAddr  rangeStart = 0;
298         tPAddr  expected = 0;
299         void    *expected_node = NULL, *tmpnode = NULL;
300         tVAddr  curPos;
301         Uint    page;
302         const tPAddr    MASK = ~0xF78;
303         
304         Start >>= 12;   End >>= 12;
305         
306         #if 0
307         Log("Directory Entries:");
308         for(page = Start >> 10;
309                 page < (End >> 10)+1;
310                 page ++)
311         {
312                 if(gaPageDir[page])
313                 {
314                         Log(" 0x%08x-0x%08x :: 0x%08x",
315                                 page<<22, ((page+1)<<22)-1,
316                                 gaPageDir[page]&~0xFFF
317                                 );
318                 }
319         }
320         #endif
321         
322         Log("Table Entries:");
323         for(page = Start, curPos = Start<<12;
324                 page < End;
325                 curPos += 0x1000, page++)
326         {
327                 if( !(gaPageDir[curPos>>22] & PF_PRESENT)
328                 ||  !(gaPageTable[page] & PF_PRESENT)
329                 ||  (gaPageTable[page] & MASK) != expected
330                 ||  (tmpnode=NULL,MM_GetPageNode(expected, &tmpnode), tmpnode != expected_node))
331                 {
332                         if(expected) {
333                                 tPAddr  orig = gaPageTable[rangeStart>>12];
334                                 MM_DumpTables_Print(rangeStart, orig, curPos - rangeStart, expected_node);
335                                 expected = 0;
336                         }
337                         if( !(gaPageDir[curPos>>22] & PF_PRESENT) )     continue;
338                         if( !(gaPageTable[curPos>>12] & PF_PRESENT) )   continue;
339                         
340                         expected = (gaPageTable[page] & MASK);
341                         MM_GetPageNode(expected, &expected_node);
342                         rangeStart = curPos;
343                 }
344                 if(expected && (expected & ~(PAGE_SIZE-1)) != giMM_ZeroPage)
345                         expected += 0x1000;
346         }
347         
348         if(expected) {
349                 tPAddr  orig = gaPageTable[rangeStart>>12];
350                 MM_DumpTables_Print(rangeStart, orig, curPos - rangeStart, expected_node);
351                 expected = 0;
352         }
353 }
354
355 /**
356  * \fn tPAddr MM_Allocate(tVAddr VAddr)
357  */
358 tPAddr MM_Allocate(volatile void * VAddr)
359 {
360         tPAddr  paddr = MM_AllocPhys();
361         if( MM_Map(VAddr, paddr) )
362         {
363                 return paddr;
364         }
365         
366         // Error of some form, either an overwrite or OOM
367         MM_DerefPhys(paddr);
368         
369         // Check for overwrite
370         paddr = MM_GetPhysAddr(VAddr);
371         if( paddr != 0 ) {
372                 Warning("MM_Allocate - Allocating to used address (%p)", VAddr);
373                 return paddr;
374         }
375         
376         // OOM
377         Warning("MM_Allocate - Out of Memory (Called by %p)", __builtin_return_address(0));
378         return 0;
379 }
380
381 void MM_AllocateZero(volatile void *VAddr)
382 {
383         if( MM_GetPhysAddr(VAddr) ) {
384                 Warning("MM_AllocateZero - Attempted overwrite at %p", VAddr);
385                 return ;
386         }
387         if( !giMM_ZeroPage )
388         {
389                 SHORTLOCK(&glMM_ZeroPage);
390                 // Check again within the lock (just in case we lost the race)
391                 if( giMM_ZeroPage == 0 )
392                 {
393                         giMM_ZeroPage = MM_Allocate(VAddr);
394                         // - Reference a second time to prevent it from being freed
395                         MM_RefPhys(giMM_ZeroPage);
396                         memset((void*)VAddr, 0, PAGE_SIZE);
397                 }
398                 SHORTREL(&glMM_ZeroPage);
399         }
400         else
401         {
402                 MM_Map(VAddr, giMM_ZeroPage);
403                 MM_RefPhys(giMM_ZeroPage);
404         }
405         MM_SetFlags(VAddr, MM_PFLAG_COW, MM_PFLAG_COW);
406 }
407
408 /**
409  * \fn int MM_Map(tVAddr VAddr, tPAddr PAddr)
410  * \brief Map a physical page to a virtual one
411  */
412 int MM_Map(volatile void *VAddr, tPAddr PAddr)
413 {
414         Uint    pagenum = (tVAddr)VAddr >> 12;
415         
416         #if TRACE_MAPS
417         Debug("MM_Map(%p, %P)", VAddr, PAddr);
418         #endif
419
420         // Sanity check
421         if( (PAddr & 0xFFF) || ((tVAddr)VAddr & 0xFFF) ) {
422                 Log_Warning("MM_Virt", "MM_Map - Physical or Virtual Addresses are not aligned (%P and %p) - %p",
423                         PAddr, VAddr, __builtin_return_address(0));
424                 //LEAVE('i', 0);
425                 return 0;
426         }
427         
428         bool    is_user = ((tVAddr)VAddr < MM_USER_MAX);
429
430         // Check if the directory is mapped
431         if( gaPageDir[ pagenum >> 10 ] == 0 )
432         {
433                 tPAddr  tmp = MM_AllocPhys();
434                 if( tmp == 0 )
435                         return 0;
436                 gaPageDir[ pagenum >> 10 ] = tmp | 3 | (is_user ? PF_USER : 0);
437                 
438                 INVLPG( &gaPageTable[ pagenum & ~0x3FF ] );
439                 memsetd( &gaPageTable[ pagenum & ~0x3FF ], 0, 1024 );
440         }
441         // Check if the page is already allocated
442         else if( gaPageTable[ pagenum ] != 0 ) {
443                 Warning("MM_Map - Allocating to used address");
444                 //LEAVE('i', 0);
445                 return 0;
446         }
447         
448         // Map
449         gaPageTable[ pagenum ] = PAddr | 3 | (is_user ? PF_USER : 0);
450         
451         INVLPG( VAddr );
452         
453         return 1;
454 }
455
456 /*
457  * A.k.a MM_Unmap
458  */
459 void MM_Deallocate(volatile void *VAddr)
460 {
461         Uint    pagenum = (tVAddr)VAddr >> 12;
462         if( gaPageDir[pagenum>>10] == 0 ) {
463                 Warning("MM_Deallocate - Directory not mapped");
464                 return;
465         }
466         
467         if(gaPageTable[pagenum] == 0) {
468                 Warning("MM_Deallocate - Page is not allocated");
469                 return;
470         }
471         
472         // Dereference and clear page
473         tPAddr  paddr = gaPageTable[pagenum] & ~0xFFF;
474         gaPageTable[pagenum] = 0;
475         MM_DerefPhys( paddr );
476 }
477
478 /**
479  * \fn tPAddr MM_GetPhysAddr(tVAddr Addr)
480  * \brief Checks if the passed address is accesable
481  */
482 tPAddr MM_GetPhysAddr(volatile const void *Addr)
483 {
484         tVAddr  addr = (tVAddr)Addr;
485         if( !(gaPageDir[addr >> 22] & 1) )
486                 return 0;
487         if( !(gaPageTable[addr >> 12] & 1) )
488                 return 0;
489         return (gaPageTable[addr >> 12] & ~0xFFF) | (addr & 0xFFF);
490 }
491
492 /**
493  * \fn void MM_SetCR3(Uint CR3)
494  * \brief Sets the current process space
495  */
496 void MM_SetCR3(Uint CR3)
497 {
498         __ASM__("mov %0, %%cr3"::"r"(CR3));
499 }
500
501 /**
502  * \brief Clear user's address space
503  */
504 void MM_ClearUser(void)
505 {
506         Uint    i, j;
507         
508         for( i = 0; i < (MM_USER_MAX>>22); i ++ )
509         {
510                 // Check if directory is not allocated
511                 if( !(gaPageDir[i] & PF_PRESENT) ) {
512                         gaPageDir[i] = 0;
513                         continue;
514                 }
515                 
516                 // Deallocate tables
517                 for( j = 0; j < 1024; j ++ )
518                 {
519                         if( gaPageTable[i*1024+j] & 1 )
520                                 MM_DerefPhys( gaPageTable[i*1024+j] & ~0xFFF );
521                         gaPageTable[i*1024+j] = 0;
522                 }
523                 
524                 // Deallocate directory
525                 MM_DerefPhys( gaPageDir[i] & ~0xFFF );
526                 gaPageDir[i] = 0;
527                 INVLPG( &gaPageTable[i*1024] );
528         }
529         INVLPG( gaPageDir );
530 }
531
532 /**
533  * \brief Deallocate an address space
534  */
535 void MM_ClearSpace(Uint32 CR3)
536 {
537          int    i, j;
538         
539         if(CR3 == (*gpPageCR3 & ~0xFFF)) {
540                 Log_Error("MMVirt", "Can't clear current address space");
541                 return ;
542         }
543
544         if( MM_GetRefCount(CR3) > 1 ) {
545                 MM_DerefPhys(CR3);
546                 Log_Log("MMVirt", "CR3 %P is still referenced, not cleaning (but dereferenced)", CR3);
547                 return ;
548         }
549
550         Log_Debug("MMVirt", "Clearing out address space 0x%x from 0x%x", CR3, *gpPageCR3);
551         
552         GET_TEMP_MAPPING(CR3);
553         INVLPG( gaTmpDir );
554
555         for( i = 0; i < 1024; i ++ )
556         {
557                 Uint32  *table = &gaTmpTable[i*1024];
558                 if( !(gaTmpDir[i] & PF_PRESENT) )
559                         continue ;
560
561                 INVLPG( table );        
562
563                 if( i < 768 || (i > MM_KERNEL_STACKS >> 22 && i < MM_KERNEL_STACKS_END >> 22) )
564                 {
565                         for( j = 0; j < 1024; j ++ )
566                         {
567                                 if( !(table[j] & 1) )
568                                         continue;
569                                 MM_DerefPhys( table[j] & ~0xFFF );
570                         }
571                 }
572
573                 if( i != (PAGE_TABLE_ADDR >> 22) )
574                 {               
575                         MM_DerefPhys( gaTmpDir[i] & ~0xFFF );
576                 }
577         }
578
579
580         MM_DerefPhys( CR3 );
581
582         REL_TEMP_MAPPING();
583 }
584
585 /**
586  * \fn tPAddr MM_Clone(void)
587  * \brief Clone the current address space
588  */
589 tPAddr MM_Clone(int bNoUserCopy)
590 {
591         Uint    i, j;
592         tPAddr  ret;
593         Uint    page = 0;
594         tVAddr  kStackBase = Proc_GetCurThread()->KernelStack - MM_KERNEL_STACK_SIZE;
595         
596         // Create Directory Table
597         ret = MM_AllocPhys();
598         if( ret == 0 ) {
599                 return 0;
600         }
601         
602         // Map
603         GET_TEMP_MAPPING( ret );
604         INVLPG( gaTmpDir );
605         memsetd( gaTmpDir, 0, 1024 );
606         
607         if( Threads_GetPID() != 0 && !bNoUserCopy )
608         {       
609                 // Copy Tables
610                 for( i = 0; i < 768; i ++)
611                 {
612                         // Check if table is allocated
613                         if( !(gaPageDir[i] & PF_PRESENT) ) {
614                                 gaTmpDir[i] = 0;
615                                 page += 1024;
616                                 continue;
617                         }
618                         
619                         // Allocate new table
620                         gaTmpDir[i] = MM_AllocPhys() | (gaPageDir[i] & 7);
621                         INVLPG( &gaTmpTable[page] );
622                         // Fill
623                         for( j = 0; j < 1024; j ++, page++ )
624                         {
625                                 if( !(gaPageTable[page] & PF_PRESENT) ) {
626                                         gaTmpTable[page] = 0;
627                                         continue;
628                                 }
629                                 
630                                 // Refrence old page
631                                 MM_RefPhys( gaPageTable[page] & ~0xFFF );
632                                 // Add to new table
633                                 if(gaPageTable[page] & PF_WRITE) {
634                                         gaTmpTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW;
635                                         gaPageTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW;
636                                         INVLPG( page << 12 );
637                                 }
638                                 else
639                                         gaTmpTable[page] = gaPageTable[page];
640                         }
641                 }
642         }
643         
644         // Map in kernel tables (and make fractal mapping)
645         for( i = 768; i < 1024; i ++ )
646         {
647                 // Fractal
648                 if( i == (PAGE_TABLE_ADDR >> 22) ) {
649                         gaTmpDir[ PAGE_TABLE_ADDR >> 22 ] = *gpTmpCR3;
650                         continue;
651                 }
652                 if( i == (TMP_TABLE_ADDR >> 22) ) {
653                         gaTmpDir[ TMP_TABLE_ADDR >> 22 ] = 0;
654                         continue ;
655                 }
656                 
657                 if( gaPageDir[i] == 0 ) {
658                         gaTmpDir[i] = 0;
659                         continue;
660                 }
661                 
662                 //LOG("gaPageDir[%x/4] = 0x%x", i*4, gaPageDir[i]);
663                 MM_RefPhys( gaPageDir[i] & ~0xFFF );
664                 gaTmpDir[i] = gaPageDir[i];
665         }
666         
667         // Allocate kernel stack
668         for(i = MM_KERNEL_STACKS >> 22; i < MM_KERNEL_STACKS_END >> 22; i ++ )
669         {
670                 // Check if directory is allocated
671                 if( (gaPageDir[i] & 1) == 0 ) {
672                         gaTmpDir[i] = 0;
673                         continue;
674                 }               
675                 
676                 // We don't care about other kernel stacks, just the current one
677                 if( i != kStackBase >> 22 ) {
678                         MM_DerefPhys( gaPageDir[i] & ~0xFFF );
679                         gaTmpDir[i] = 0;
680                         continue;
681                 }
682                 
683                 // Create a copy
684                 gaTmpDir[i] = MM_AllocPhys() | 3;
685                 INVLPG( &gaTmpTable[i*1024] );
686                 for( j = 0; j < 1024; j ++ )
687                 {
688                         // Is the page allocated? If not, skip
689                         if( !(gaPageTable[i*1024+j] & 1) ) {
690                                 gaTmpTable[i*1024+j] = 0;
691                                 continue;
692                         }
693                         
694                         // We don't care about other kernel stacks
695                         if( ((i*1024+j)*4096 & ~(MM_KERNEL_STACK_SIZE-1)) != kStackBase ) {
696                                 gaTmpTable[i*1024+j] = 0;
697                                 continue;
698                         }
699                         
700                         // Allocate page
701                         gaTmpTable[i*1024+j] = MM_AllocPhys() | 3;
702                         
703                         void *tmp = MM_MapTemp( gaTmpTable[i*1024+j] & ~0xFFF );
704                         memcpy( tmp, (void *)( (i*1024+j)*PAGE_SIZE ), PAGE_SIZE );
705                         MM_FreeTemp( tmp );
706                 }
707         }
708         
709         REL_TEMP_MAPPING();
710         
711         //LEAVE('x', ret);
712         return ret;
713 }
714
715 /**
716  * \fn tVAddr MM_NewKStack(void)
717  * \brief Create a new kernel stack
718  */
719 tVAddr MM_NewKStack(void)
720 {
721         for(tVAddr base = MM_KERNEL_STACKS; base < MM_KERNEL_STACKS_END; base += MM_KERNEL_STACK_SIZE)
722         {
723                 tPage   *pageptr = (void*)base;
724                 // Check if space is free
725                 if(MM_GetPhysAddr(pageptr) != 0)
726                         continue;
727                 // Allocate
728                 for(Uint i = 0; i < MM_KERNEL_STACK_SIZE/PAGE_SIZE; i ++ )
729                 {
730                         if( MM_Allocate(pageptr + i) == 0 )
731                         {
732                                 // On error, print a warning and return error
733                                 Warning("MM_NewKStack - Out of memory");
734                                 // - Clean up
735                                 //for( i += 0x1000 ; i < MM_KERNEL_STACK_SIZE; i += 0x1000 )
736                                 //      MM_Deallocate(base+i);
737                                 return 0;
738                         }
739                 }
740                 // Success
741 //              Log("MM_NewKStack - Allocated %p", base + MM_KERNEL_STACK_SIZE);
742                 return base+MM_KERNEL_STACK_SIZE;
743         }
744         // No stacks left
745         Log_Warning("MMVirt", "MM_NewKStack - No address space left");
746         return 0;
747 }
748
749 /**
750  * \fn tVAddr MM_NewWorkerStack()
751  * \brief Creates a new worker stack
752  */
753 tVAddr MM_NewWorkerStack(Uint *StackContents, size_t ContentsSize)
754 {
755         Uint    base;
756         tPAddr  page;
757         
758         LOG("(StackContents=%p,ContentsSize=%i)", StackContents, ContentsSize);
759         // TODO: Thread safety
760         // Find a free worker stack address
761         for(base = giLastUsedWorker; base < NUM_WORKER_STACKS; base++)
762         {
763                 // Used block
764                 if( gWorkerStacks[base/32] == -1 ) {
765                         base += 31;     base &= ~31;
766                         base --;        // Counteracted by the base++
767                         continue;
768                 }
769                 // Used stack
770                 if( gWorkerStacks[base/32] & (1 << base) ) {
771                         continue;
772                 }
773                 break;
774         }
775         if(base >= NUM_WORKER_STACKS) {
776                 Log_Error("MMVirt", "Uh-oh! Out of worker stacks");
777                 return 0;
778         }
779         LOG("base=0x%x", base);
780         
781         // It's ours now!
782         gWorkerStacks[base/32] |= (1 << base);
783         // Make life easier for later calls
784         giLastUsedWorker = base;
785         // We have one
786         base = WORKER_STACKS + base * WORKER_STACK_SIZE;
787         //Log(" MM_NewWorkerStack: base = 0x%x", base);
788         LOG("base=%p (top)", base);
789         
790         // Set the temp fractals to TID0's address space
791         GET_TEMP_MAPPING( ((Uint)gaInitPageDir - KERNEL_BASE) );
792         INVLPG( gaTmpDir );
793         
794         // Check if the directory is mapped (we are assuming that the stacks
795         // will fit neatly in a directory)
796         LOG("gaTmpDir[ 0x%x ] = 0x%x", base>>22, gaTmpDir[ base >> 22 ]);
797         if(gaTmpDir[ base >> 22 ] == 0) {
798                 gaTmpDir[ base >> 22 ] = MM_AllocPhys() | 3;
799                 INVLPG( &gaTmpTable[ (base>>12) & ~0x3FF ] );
800         }
801         
802         // Mapping Time!
803         for( Uint addr = 0; addr < WORKER_STACK_SIZE; addr += 0x1000 )
804         {
805                 page = MM_AllocPhys();
806                 gaTmpTable[ (base + addr) >> 12 ] = page | 3;
807         }
808         LOG("mapped");
809
810         // Release temporary fractal
811         REL_TEMP_MAPPING();
812
813         // NOTE: Max of 1 page
814         // `page` is the last allocated page from the previious for loop
815         LOG("Mapping first page");
816         char    *tmpPage = MM_MapTemp( page );
817         LOG("tmpPage=%p", tmpPage);
818         memcpy( tmpPage + (0x1000 - ContentsSize), StackContents, ContentsSize);
819         MM_FreeTemp( tmpPage );
820         
821         //Log("MM_NewWorkerStack: RETURN 0x%x", base);
822         LOG("return %p", base+WORKER_STACK_SIZE);
823         return base + WORKER_STACK_SIZE;
824 }
825
826 /**
827  * \fn void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
828  * \brief Sets the flags on a page
829  */
830 void MM_SetFlags(volatile void *VAddr, Uint Flags, Uint Mask)
831 {
832         Uint    pagenum = (tVAddr)VAddr >> 12;
833         if( !(gaPageDir[pagenum >> 10] & 1) )   return ;
834         if( !(gaPageTable[pagenum] & 1) )       return ;
835         
836         tTabEnt *ent = &gaPageTable[pagenum];
837         
838         // Read-Only
839         if( Mask & MM_PFLAG_RO )
840         {
841                 if( Flags & MM_PFLAG_RO ) {
842                         *ent &= ~PF_WRITE;
843                 }
844                 else {
845                         gaPageDir[pagenum >> 10] |= PF_WRITE;
846                         *ent |= PF_WRITE;
847                 }
848         }
849         
850         // Kernel
851         if( Mask & MM_PFLAG_KERNEL )
852         {
853                 if( Flags & MM_PFLAG_KERNEL ) {
854                         *ent &= ~PF_USER;
855                 }
856                 else {
857                         gaPageDir[pagenum >> 10] |= PF_USER;
858                         *ent |= PF_USER;
859                 }
860         }
861         
862         // Copy-On-Write
863         if( Mask & MM_PFLAG_COW )
864         {
865                 if( Flags & MM_PFLAG_COW ) {
866                         *ent &= ~PF_WRITE;
867                         *ent |= PF_COW;
868                 }
869                 else {
870                         *ent &= ~PF_COW;
871                         *ent |= PF_WRITE;
872                 }
873         }
874         
875         //Log("MM_SetFlags: *ent = 0x%08x, gaPageDir[%i] = 0x%08x",
876         //      *ent, VAddr >> 22, gaPageDir[VAddr >> 22]);
877 }
878
879 /**
880  * \brief Get the flags on a page
881  */
882 Uint MM_GetFlags(volatile const void *VAddr)
883 {
884         Uint    pagenum = (tVAddr)VAddr >> 12;
885         
886         // Validity Check
887         if( !(gaPageDir[pagenum >> 10] & 1) )   return 0;
888         if( !(gaPageTable[pagenum] & 1) )       return 0;
889         
890         tTabEnt *ent = &gaPageTable[pagenum];
891         
892         Uint    ret = 0;
893         // Read-Only
894         if( !(*ent & PF_WRITE) )        ret |= MM_PFLAG_RO;
895         // Kernel
896         if( !(*ent & PF_USER) ) ret |= MM_PFLAG_KERNEL;
897         // Copy-On-Write
898         if( *ent & PF_COW )     ret |= MM_PFLAG_COW;
899         
900         return ret;
901 }
902
903 /**
904  * \brief Check if the provided buffer is valid
905  * \return Boolean valid
906  */
907 int MM_IsValidBuffer(tVAddr Addr, size_t Size)
908 {
909          int    bIsUser;
910          int    dir, tab;
911
912         Size += Addr & (PAGE_SIZE-1);
913         Addr &= ~(PAGE_SIZE-1);
914
915         dir = Addr >> 22;
916         tab = Addr >> 12;
917         
918 //      Debug("Addr = %p, Size = 0x%x, dir = %i, tab = %i", Addr, Size, dir, tab);
919
920         if( !(gaPageDir[dir] & 1) )     return 0;
921         if( !(gaPageTable[tab] & 1) )   return 0;
922         
923         bIsUser = !!(gaPageTable[tab] & PF_USER);
924
925         while( Size >= PAGE_SIZE )
926         {
927                 if( (tab & 1023) == 0 )
928                 {
929                         dir ++;
930                         if( !(gaPageDir[dir] & 1) )     return 0;
931                 }
932                 
933                 if( !(gaPageTable[tab] & 1) )   return 0;
934                 if( bIsUser && !(gaPageTable[tab] & PF_USER) )  return 0;
935
936                 tab ++;
937                 Size -= PAGE_SIZE;
938         }
939         return 1;
940 }
941
942 /**
943  * \fn tPAddr MM_DuplicatePage(tVAddr VAddr)
944  * \brief Duplicates a virtual page to a physical one
945  */
946 tPAddr MM_DuplicatePage(tVAddr VAddr)
947 {
948         tPAddr  ret;
949         void    *temp;
950          int    wasRO = 0;
951         
952         //ENTER("xVAddr", VAddr);
953         
954         // Check if mapped
955         if( !(gaPageDir  [VAddr >> 22] & PF_PRESENT) )  return 0;
956         if( !(gaPageTable[VAddr >> 12] & PF_PRESENT) )  return 0;
957         
958         // Page Align
959         VAddr &= ~0xFFF;
960         
961         // Allocate new page
962         ret = MM_AllocPhys();
963         if( !ret ) {
964                 return 0;
965         }
966         
967         // Write-lock the page (to keep data constistent), saving its R/W state
968         wasRO = (gaPageTable[VAddr >> 12] & PF_WRITE ? 0 : 1);
969         gaPageTable[VAddr >> 12] &= ~PF_WRITE;
970         INVLPG( VAddr );
971         
972         // Copy Data
973         temp = MM_MapTemp(ret);
974         memcpy( temp, (void*)VAddr, 0x1000 );
975         MM_FreeTemp(temp);
976         
977         // Restore Writeable status
978         if(!wasRO)      gaPageTable[VAddr >> 12] |= PF_WRITE;
979         INVLPG(VAddr);
980         
981         //LEAVE('X', ret);
982         return ret;
983 }
984
985 /**
986  * \fn Uint MM_MapTemp(tPAddr PAddr)
987  * \brief Create a temporary memory mapping
988  * \todo Show Luigi Barone (C Lecturer) and see what he thinks
989  */
990 void *MM_MapTemp(tPAddr PAddr)
991 {
992         ENTER("PPAddr", PAddr);
993         
994         PAddr &= ~0xFFF;
995         
996         if( Semaphore_Wait(&gTempMappingsSem, 1) != 1 )
997                 return NULL;
998         LOG("Semaphore good");
999         Mutex_Acquire( &glTempMappings );
1000         for( int i = 0; i < NUM_TEMP_PAGES; i ++ )
1001         {
1002                 Uint32  *pte = &gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ];
1003                 LOG("%i: %x", i, *pte);
1004                 // Check if page used
1005                 if(*pte & 1)    continue;
1006                 // Mark as used
1007                 *pte = PAddr | 3;
1008                 INVLPG( TEMP_MAP_ADDR + (i << 12) );
1009                 LEAVE('p', TEMP_MAP_ADDR + (i << 12));
1010                 Mutex_Release( &glTempMappings );
1011                 return (void*)( TEMP_MAP_ADDR + (i << 12) );
1012         }
1013         Mutex_Release( &glTempMappings );
1014         Log_KernelPanic("MMVirt", "Semaphore suplied a mapping, but none are avaliable");
1015         return NULL;
1016 }
1017
1018 /**
1019  * \fn void MM_FreeTemp(tVAddr PAddr)
1020  * \brief Free's a temp mapping
1021  */
1022 void MM_FreeTemp(void *VAddr)
1023 {
1024          int    i = (tVAddr)VAddr >> 12;
1025         //ENTER("xVAddr", VAddr);
1026         
1027         if(i >= (TEMP_MAP_ADDR >> 12)) {
1028                 gaPageTable[ i ] = 0;
1029                 Semaphore_Signal(&gTempMappingsSem, 1);
1030         }
1031         
1032         //LEAVE('-');
1033 }
1034
1035 /**
1036  * \fn tVAddr MM_MapHWPages(tPAddr PAddr, Uint Number)
1037  * \brief Allocates a contigous number of pages
1038  */
1039 void *MM_MapHWPages(tPAddr PAddr, Uint Number)
1040 {
1041          int    j;
1042         
1043         PAddr &= ~0xFFF;
1044
1045         if( PAddr < 1024*1024 && (1024*1024-PAddr) >= Number * PAGE_SIZE )
1046         {
1047                 return (void*)(KERNEL_BASE + PAddr);
1048         }
1049
1050         // Scan List
1051         for( int i = 0; i < NUM_HW_PAGES; i ++ )
1052         {               
1053                 // Check if addr used
1054                 if( gaPageTable[ (HW_MAP_ADDR >> 12) + i ] & 1 )
1055                         continue;
1056                 
1057                 // Check possible region
1058                 for( j = 0; j < Number && i + j < NUM_HW_PAGES; j ++ )
1059                 {
1060                         // If there is an allocated page in the region we are testing, break
1061                         if( gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] & 1 )    break;
1062                 }
1063                 // Is it all free?
1064                 if( j == Number )
1065                 {
1066                         // Allocate
1067                         for( j = 0; j < Number; j++ ) {
1068                                 MM_RefPhys( PAddr + (j<<12) );
1069                                 gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] = (PAddr + (j<<12)) | 3;
1070                         }
1071                         return (void*)(HW_MAP_ADDR + (i<<12));
1072                 }
1073         }
1074         // If we don't find any, return NULL
1075         return 0;
1076 }
1077
1078 /**
1079  * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
1080  * \brief Allocates DMA physical memory
1081  * \param Pages Number of pages required
1082  * \param MaxBits       Maximum number of bits the physical address can have
1083  * \param PhysAddr      Pointer to the location to place the physical address allocated
1084  * \return Virtual address allocate
1085  */
1086 void *MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
1087 {
1088         tPAddr  phys;
1089         void    *ret;
1090         
1091         ENTER("iPages iMaxBits pPhysAddr", Pages, MaxBits, PhysAddr);
1092         
1093         if(MaxBits == -1)
1094                 MaxBits = PHYS_BITS;
1095         
1096         // Sanity Check
1097         if(MaxBits < 12) {
1098                 LEAVE('i', 0);
1099                 return 0;
1100         }
1101         
1102         // Fast Allocate
1103         if(Pages == 1 && MaxBits >= PHYS_BITS)
1104         {
1105                 phys = MM_AllocPhys();
1106                 if( PhysAddr )
1107                         *PhysAddr = phys;
1108                 if( !phys ) {
1109                         LEAVE_RET('i', 0);
1110                 }
1111                 ret = MM_MapHWPages(phys, 1);
1112                 if(ret == 0) {
1113                         MM_DerefPhys(phys);
1114                         LEAVE('i', 0);
1115                         return 0;
1116                 }
1117                 LEAVE('x', ret);
1118                 return (void*)ret;
1119         }
1120         
1121         // Slow Allocate
1122         phys = MM_AllocPhysRange(Pages, MaxBits);
1123         // - Was it allocated?
1124         if(phys == 0) {
1125                 LEAVE('i', 0);
1126                 return 0;
1127         }
1128         
1129         // Allocated successfully, now map
1130         ret = MM_MapHWPages(phys, Pages);
1131         // - MapHWPages references the memory, so release references
1132         for( int i = 0; i < Pages; i ++ )
1133                 MM_DerefPhys(phys + i*PAGE_SIZE);
1134         if( ret == 0 ) {
1135                 LEAVE('i', 0);
1136                 return 0;
1137         }
1138         
1139         if( PhysAddr )
1140                 *PhysAddr = phys;
1141         LEAVE('x', ret);
1142         return (void*)ret;
1143 }
1144
1145 /**
1146  * \fn void MM_UnmapHWPages(tVAddr VAddr, Uint Number)
1147  * \brief Unmap a hardware page
1148  */
1149 void MM_UnmapHWPages(volatile void *Base, Uint Number)
1150 {
1151         tVAddr  VAddr = (tVAddr)Base;
1152         //Log_Debug("VirtMem", "MM_UnmapHWPages: (VAddr=0x%08x, Number=%i)", VAddr, Number);
1153
1154         //
1155         if( KERNEL_BASE <= VAddr && VAddr < KERNEL_BASE + 1024*1024 )
1156                 return ;
1157         
1158         Uint pagenum = VAddr >> 12;
1159
1160         // Sanity Check
1161         if(VAddr < HW_MAP_ADDR || VAddr+Number*0x1000 > HW_MAP_MAX)     return;
1162         
1163         
1164         Mutex_Acquire( &glTempMappings );       // Temp and HW share a directory, so they share a lock
1165         
1166         for( Uint i = 0; i < Number; i ++ )
1167         {
1168                 MM_DerefPhys( gaPageTable[ pagenum + i ] & ~0xFFF );
1169                 gaPageTable[ pagenum + i ] = 0;
1170                 INVLPG( (tVAddr)(pagenum + i) << 12 );
1171         }
1172         
1173         Mutex_Release( &glTempMappings );
1174 }
1175

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