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

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