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

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