058fe00735a9cec5783a01c38b94957b4924592d
[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   1
13 #include <acess.h>
14 #include <mm_phys.h>
15 #include <proc.h>
16
17 #define KERNEL_STACKS           0xF0000000
18 #define KERNEL_STACK_SIZE       0x00008000
19 #define KERNEL_STACKS_END       0xFD000000
20 #define WORKER_STACKS           0x00100000      // Thread0 Only!
21 #define WORKER_STACK_SIZE       KERNEL_STACK_SIZE
22 #define WORKER_STACKS_END       0xB0000000
23 #define NUM_WORKER_STACKS       ((WORKER_STACKS_END-WORKER_STACKS)/WORKER_STACK_SIZE)
24 #define PAGE_TABLE_ADDR 0xFD000000
25 #define PAGE_DIR_ADDR   0xFD3F4000
26 #define PAGE_CR3_ADDR   0xFD3F4FD0
27 #define TMP_CR3_ADDR    0xFD3F4FD4      // Part of core instead of temp
28 #define TMP_DIR_ADDR    0xFD3F5000      // Same
29 #define TMP_TABLE_ADDR  0xFD400000
30 #define HW_MAP_ADDR             0xFD800000
31 #define HW_MAP_MAX              0xFEFF0000
32 #define NUM_HW_PAGES    ((HW_MAP_MAX-HW_MAP_ADDR)/0x1000)
33 #define TEMP_MAP_ADDR   0xFEFF0000      // Allows 16 "temp" pages
34 #define NUM_TEMP_PAGES  16
35
36 #define PF_PRESENT      0x1
37 #define PF_WRITE        0x2
38 #define PF_USER         0x4
39 #define PF_COW          0x200
40 #define PF_PAGED        0x400
41
42 #define INVLPG(addr)    __asm__ __volatile__ ("invlpg (%0)"::"r"(addr))
43
44 #if USE_PAE
45 typedef Uint64  tTabEnt;
46 #else
47 typedef Uint32  tTabEnt;
48 #endif
49
50 // === IMPORTS ===
51 extern Uint32   gaInitPageDir[1024];
52 extern Uint32   gaInitPageTable[1024];
53 extern void     Threads_SegFault(tVAddr Addr);
54 extern void     Error_Backtrace(Uint eip, Uint ebp);
55
56 // === PROTOTYPES ===
57 void    MM_PreinitVirtual();
58 void    MM_InstallVirtual();
59 void    MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs);
60 void    MM_DumpTables(tVAddr Start, tVAddr End);
61 tPAddr  MM_DuplicatePage(tVAddr VAddr);
62
63 // === GLOBALS ===
64 #define gaPageTable     ((tTabEnt*)PAGE_TABLE_ADDR)
65 #define gaPageDir       ((tTabEnt*)PAGE_DIR_ADDR)
66 #define gaPageCR3       ((tTabEnt*)PAGE_CR3_ADDR)
67 #define gaTmpTable      ((tTabEnt*)TMP_TABLE_ADDR)
68 #define gaTmpDir        ((tTabEnt*)TMP_DIR_ADDR)
69 #define gTmpCR3 ((tTabEnt*)TMP_CR3_ADDR)
70 //tPAddr        *gaPageTable = (void*)PAGE_TABLE_ADDR;
71 //tPAddr        *gaPageDir = (void*)PAGE_DIR_ADDR;
72 //tPAddr        *gaPageCR3 = (void*)PAGE_CR3_ADDR;
73 //tPAddr        *gaTmpTable = (void*)TMP_TABLE_ADDR;
74 //tPAddr        *gaTmpDir = (void*)TMP_DIR_ADDR;
75 //tPAddr        *gTmpCR3 = (void*)TMP_CR3_ADDR;
76  int    gilTempMappings = 0;
77  int    gilTempFractal = 0;
78 Uint32  gWorkerStacks[(NUM_WORKER_STACKS+31)/32];
79  int    giLastUsedWorker = 0;
80
81 // === CODE ===
82 /**
83  * \fn void MM_PreinitVirtual()
84  * \brief Maps the fractal mappings
85  */
86 void MM_PreinitVirtual()
87 {
88         gaInitPageDir[ 0 ] = 0;
89         gaInitPageDir[ PAGE_TABLE_ADDR >> 22 ] = ((Uint)&gaInitPageDir - KERNEL_BASE) | 3;
90         INVLPG( PAGE_TABLE_ADDR );
91 }
92
93 /**
94  * \fn void MM_InstallVirtual()
95  * \brief Sets up the constant page mappings
96  */
97 void MM_InstallVirtual()
98 {
99          int    i;
100         
101         // --- Pre-Allocate kernel tables
102         for( i = KERNEL_BASE>>22; i < 1024; i ++ )
103         {
104                 if( gaPageDir[ i ] )    continue;
105                 // Skip stack tables, they are process unique
106                 if( i > KERNEL_STACKS >> 22 && i < KERNEL_STACKS_END >> 22) {
107                         gaPageDir[ i ] = 0;
108                         continue;
109                 }
110                 // Preallocate table
111                 gaPageDir[ i ] = MM_AllocPhys() | 3;
112                 INVLPG( &gaPageTable[i*1024] );
113                 memset( &gaPageTable[i*1024], 0, 0x1000 );
114         }
115 }
116
117 /**
118  * \fn void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
119  * \brief Called on a page fault
120  */
121 void MM_PageFault(tVAddr Addr, Uint ErrorCode, tRegs *Regs)
122 {
123         //ENTER("xAddr bErrorCode", Addr, ErrorCode);
124         
125         // -- Check for COW --
126         if( gaPageDir  [Addr>>22] & PF_PRESENT
127          && gaPageTable[Addr>>12] & PF_PRESENT
128          && gaPageTable[Addr>>12] & PF_COW )
129         {
130                 tPAddr  paddr;
131                 if(MM_GetRefCount( gaPageTable[Addr>>12] & ~0xFFF ) == 1)
132                 {
133                         gaPageTable[Addr>>12] &= ~PF_COW;
134                         gaPageTable[Addr>>12] |= PF_PRESENT|PF_WRITE;
135                 }
136                 else
137                 {
138                         paddr = MM_DuplicatePage( Addr );
139                         MM_DerefPhys( gaPageTable[Addr>>12] & ~0xFFF );
140                         gaPageTable[Addr>>12] &= PF_USER;
141                         gaPageTable[Addr>>12] |= paddr|PF_PRESENT|PF_WRITE;
142                 }
143                 
144                 INVLPG( Addr & ~0xFFF );
145                 //LEAVE('-')
146                 return;
147         }
148         
149         // If it was a user, tell the thread handler
150         if(ErrorCode & 4) {
151                 Warning("%s %s %s memory%s",
152                         (ErrorCode&4?"User":"Kernel"),
153                         (ErrorCode&2?"write to":"read from"),
154                         (ErrorCode&1?"bad/locked":"non-present"),
155                         (ErrorCode&16?" (Instruction Fetch)":"")
156                         );
157                 Warning("User Pagefault: Instruction at %p accessed %p", Regs->eip, Addr);
158                 __asm__ __volatile__ ("sti");   // Restart IRQs
159                 Threads_SegFault(Addr);
160                 return ;
161         }
162         
163         // -- Check Error Code --
164         if(ErrorCode & 8)
165                 Warning("Reserved Bits Trashed!");
166         else
167         {
168                 Warning("%s %s %s memory%s",
169                         (ErrorCode&4?"User":"Kernel"),
170                         (ErrorCode&2?"write to":"read from"),
171                         (ErrorCode&1?"bad/locked":"non-present"),
172                         (ErrorCode&16?" (Instruction Fetch)":"")
173                         );
174         }
175         
176         Log("Code at %p accessed %p", Regs->eip, Addr);
177         // Print Stack Backtrace
178         Error_Backtrace(Regs->eip, Regs->ebp);
179         
180         Log("gaPageDir[0x%x] = 0x%x", Addr>>22, gaPageDir[Addr>>22]);
181         if( gaPageDir[Addr>>22] & PF_PRESENT )
182                 Log("gaPageTable[0x%x] = 0x%x", Addr>>12, gaPageTable[Addr>>12]);
183         
184         //MM_DumpTables(0, -1); 
185         
186         Panic("Page Fault at 0x%x (Accessed 0x%x)", Regs->eip, Addr);
187 }
188
189 /**
190  * \fn void MM_DumpTables(tVAddr Start, tVAddr End)
191  * \brief Dumps the layout of the page tables
192  */
193 void MM_DumpTables(tVAddr Start, tVAddr End)
194 {
195         tVAddr  rangeStart = 0;
196         tPAddr  expected = 0;
197         tVAddr  curPos;
198         Uint    page;
199         const tPAddr    MASK = ~0xF98;
200         
201         Start >>= 12;   End >>= 12;
202         
203         #if 0
204         Log("Directory Entries:");
205         for(page = Start >> 10;
206                 page < (End >> 10)+1;
207                 page ++)
208         {
209                 if(gaPageDir[page])
210                 {
211                         Log(" 0x%08x-0x%08x :: 0x%08x",
212                                 page<<22, ((page+1)<<22)-1,
213                                 gaPageDir[page]&~0xFFF
214                                 );
215                 }
216         }
217         #endif
218         
219         Log("Table Entries:");
220         for(page = Start, curPos = Start<<12;
221                 page < End;
222                 curPos += 0x1000, page++)
223         {
224                 if( !(gaPageDir[curPos>>22] & PF_PRESENT)
225                 ||  !(gaPageTable[page] & PF_PRESENT)
226                 ||  (gaPageTable[page] & MASK) != expected)
227                 {
228                         if(expected) {
229                                 Log(" 0x%08x-0x%08x => 0x%08x-0x%08x (%s%s%s%s)",
230                                         rangeStart, curPos - 1,
231                                         gaPageTable[rangeStart>>12] & ~0xFFF,
232                                         (expected & ~0xFFF) - 1,
233                                         (expected & PF_PAGED ? "p" : "-"),
234                                         (expected & PF_COW ? "C" : "-"),
235                                         (expected & PF_USER ? "U" : "-"),
236                                         (expected & PF_WRITE ? "W" : "-")
237                                         );
238                                 expected = 0;
239                         }
240                         if( !(gaPageDir[curPos>>22] & PF_PRESENT) )     continue;
241                         if( !(gaPageTable[curPos>>12] & PF_PRESENT) )   continue;
242                         
243                         expected = (gaPageTable[page] & MASK);
244                         rangeStart = curPos;
245                 }
246                 if(expected)    expected += 0x1000;
247         }
248         
249         if(expected) {
250                 Log("0x%08x-0x%08x => 0x%08x-0x%08x (%s%s%s%s)",
251                         rangeStart, curPos - 1,
252                         gaPageTable[rangeStart>>12] & ~0xFFF,
253                         (expected & ~0xFFF) - 1,
254                         (expected & PF_PAGED ? "p" : "-"),
255                         (expected & PF_COW ? "C" : "-"),
256                         (expected & PF_USER ? "U" : "-"),
257                         (expected & PF_WRITE ? "W" : "-")
258                         );
259                 expected = 0;
260         }
261 }
262
263 /**
264  * \fn tPAddr MM_Allocate(tVAddr VAddr)
265  */
266 tPAddr MM_Allocate(tVAddr VAddr)
267 {
268         tPAddr  paddr;
269         //ENTER("xVAddr", VAddr);
270         //__asm__ __volatile__ ("xchg %bx,%bx");
271         // Check if the directory is mapped
272         if( gaPageDir[ VAddr >> 22 ] == 0 )
273         {
274                 // Allocate directory
275                 paddr = MM_AllocPhys();
276                 //LOG("paddr = 0x%llx (new table)", paddr);
277                 if( paddr == 0 ) {
278                         Warning("MM_Allocate - Out of Memory (Called by %p)", __builtin_return_address(0));
279                         LEAVE('i',0);
280                         return 0;
281                 }
282                 // Map
283                 gaPageDir[ VAddr >> 22 ] = paddr | 3;
284                 // Mark as user
285                 if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER;
286                 
287                 INVLPG( &gaPageDir[ VAddr >> 22 ] );
288                 //LOG("Clearing new table");
289                 memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 );
290         }
291         // Check if the page is already allocated
292         else if( gaPageTable[ VAddr >> 12 ] != 0 ) {
293                 Warning("MM_Allocate - Allocating to used address (%p)", VAddr);
294                 //LEAVE('X', gaPageTable[ VAddr >> 12 ] & ~0xFFF);
295                 return gaPageTable[ VAddr >> 12 ] & ~0xFFF;
296         }
297         
298         // Allocate
299         paddr = MM_AllocPhys();
300         //LOG("paddr = 0x%llx", paddr);
301         if( paddr == 0 ) {
302                 Warning("MM_Allocate - Out of Memory when allocating at %p (Called by %p)",
303                         VAddr, __builtin_return_address(0));
304                 //LEAVE('i',0);
305                 return 0;
306         }
307         // Map
308         gaPageTable[ VAddr >> 12 ] = paddr | 3;
309         // Mark as user
310         if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER;
311         // Invalidate Cache for address
312         INVLPG( VAddr & ~0xFFF );
313         
314         //LEAVE('X', paddr);
315         return paddr;
316 }
317
318 /**
319  * \fn void MM_Deallocate(tVAddr VAddr)
320  */
321 void MM_Deallocate(tVAddr VAddr)
322 {
323         if( gaPageDir[ VAddr >> 22 ] == 0 ) {
324                 Warning("MM_Deallocate - Directory not mapped");
325                 return;
326         }
327         
328         if(gaPageTable[ VAddr >> 12 ] == 0) {
329                 Warning("MM_Deallocate - Page is not allocated");
330                 return;
331         }
332         
333         // Dereference page
334         MM_DerefPhys( gaPageTable[ VAddr >> 12 ] & ~0xFFF );
335         // Clear page
336         gaPageTable[ VAddr >> 12 ] = 0;
337 }
338
339 /**
340  * \fn tPAddr MM_GetPhysAddr(tVAddr Addr)
341  * \brief Checks if the passed address is accesable
342  */
343 tPAddr MM_GetPhysAddr(tVAddr Addr)
344 {
345         if( !(gaPageDir[Addr >> 22] & 1) )
346                 return 0;
347         if( !(gaPageTable[Addr >> 12] & 1) )
348                 return 0;
349         return (gaPageTable[Addr >> 12] & ~0xFFF) | (Addr & 0xFFF);
350 }
351
352
353 /**
354  * \fn int MM_IsUser(tVAddr VAddr)
355  * \brief Checks if a page is user accessable
356  */
357 int MM_IsUser(tVAddr VAddr)
358 {
359         if( !(gaPageDir[VAddr >> 22] & 1) )
360                 return 0;
361         if( !(gaPageTable[VAddr >> 12] & 1) )
362                 return 0;
363         if( !(gaPageTable[VAddr >> 12] & PF_USER) )
364                 return 0;
365         return 1;
366 }
367
368 /**
369  * \fn void MM_SetCR3(tPAddr CR3)
370  * \brief Sets the current process space
371  */
372 void MM_SetCR3(tPAddr CR3)
373 {
374         __asm__ __volatile__ ("mov %0, %%cr3"::"r"(CR3));
375 }
376
377 /**
378  * \fn int MM_Map(tVAddr VAddr, tPAddr PAddr)
379  * \brief Map a physical page to a virtual one
380  */
381 int MM_Map(tVAddr VAddr, tPAddr PAddr)
382 {
383         //ENTER("xVAddr xPAddr", VAddr, PAddr);
384         // Sanity check
385         if( PAddr & 0xFFF || VAddr & 0xFFF ) {
386                 Warning("MM_Map - Physical or Virtual Addresses are not aligned");
387                 //LEAVE('i', 0);
388                 return 0;
389         }
390         
391         // Align addresses
392         PAddr &= ~0xFFF;        VAddr &= ~0xFFF;
393         
394         // Check if the directory is mapped
395         if( gaPageDir[ VAddr >> 22 ] == 0 )
396         {
397                 gaPageDir[ VAddr >> 22 ] = MM_AllocPhys() | 3;
398                 
399                 // Mark as user
400                 if(VAddr < MM_USER_MAX) gaPageDir[ VAddr >> 22 ] |= PF_USER;
401                 
402                 INVLPG( &gaPageTable[ (VAddr >> 12) & ~0x3FF ] );
403                 memsetd( &gaPageTable[ (VAddr >> 12) & ~0x3FF ], 0, 1024 );
404         }
405         // Check if the page is already allocated
406         else if( gaPageTable[ VAddr >> 12 ] != 0 ) {
407                 Warning("MM_Map - Allocating to used address");
408                 //LEAVE('i', 0);
409                 return 0;
410         }
411         
412         // Map
413         gaPageTable[ VAddr >> 12 ] = PAddr | 3;
414         // Mark as user
415         if(VAddr < MM_USER_MAX) gaPageTable[ VAddr >> 12 ] |= PF_USER;
416         
417         //LOG("gaPageTable[ 0x%x ] = (Uint)%p = 0x%x",
418         //      VAddr >> 12, &gaPageTable[ VAddr >> 12 ], gaPageTable[ VAddr >> 12 ]);
419         
420         // Reference
421         MM_RefPhys( PAddr );
422         
423         //LOG("INVLPG( 0x%x )", VAddr);
424         INVLPG( VAddr );
425         
426         //LEAVE('i', 1);
427         return 1;
428 }
429
430 /**
431  * \fn tVAddr MM_ClearUser()
432  * \brief Clear user's address space
433  */
434 tVAddr MM_ClearUser()
435 {
436         Uint    i, j;
437         
438         // Copy Directories
439         for( i = 0; i < (MM_USER_MAX>>22); i ++ )
440         {
441                 // Check if directory is not allocated
442                 if( !(gaPageDir[i] & PF_PRESENT) ) {
443                         gaPageDir[i] = 0;
444                         continue;
445                 }
446                 
447                 
448                 for( j = 0; j < 1024; j ++ )
449                 {
450                         if( gaPageTable[i*1024+j] & 1 )
451                                 MM_DerefPhys( gaPageTable[i*1024+j] & ~0xFFF );
452                         gaPageTable[i*1024+j] = 0;
453                 }
454                 
455                 MM_DerefPhys( gaPageDir[i] & ~0xFFF );
456                 gaPageDir[i] = 0;
457                 INVLPG( &gaPageTable[i*1024] );
458         }
459         INVLPG( gaPageDir );
460         
461         return *gaPageCR3;
462 }
463
464 /**
465  * \fn tPAddr MM_Clone()
466  * \brief Clone the current address space
467  */
468 tPAddr MM_Clone()
469 {
470         Uint    i, j;
471         tVAddr  ret;
472         Uint    page = 0;
473         tVAddr  kStackBase = Proc_GetCurThread()->KernelStack - KERNEL_STACK_SIZE;
474         void    *tmp;
475         
476         LOCK( &gilTempFractal );
477         
478         // Create Directory Table
479         *gTmpCR3 = MM_AllocPhys() | 3;
480         INVLPG( gaTmpDir );
481         //LOG("Allocated Directory (%x)", *gTmpCR3);
482         memsetd( gaTmpDir, 0, 1024 );
483         
484         // Copy Tables
485         for(i=0;i<768;i++)
486         {
487                 // Check if table is allocated
488                 if( !(gaPageDir[i] & PF_PRESENT) ) {
489                         gaTmpDir[i] = 0;
490                         page += 1024;
491                         continue;
492                 }
493                 
494                 // Allocate new table
495                 gaTmpDir[i] = MM_AllocPhys() | (gaPageDir[i] & 7);
496                 INVLPG( &gaTmpTable[page] );
497                 // Fill
498                 for( j = 0; j < 1024; j ++, page++ )
499                 {
500                         if( !(gaPageTable[page] & PF_PRESENT) ) {
501                                 gaTmpTable[page] = 0;
502                                 continue;
503                         }
504                         
505                         // Refrence old page
506                         MM_RefPhys( gaPageTable[page] & ~0xFFF );
507                         // Add to new table
508                         if(gaPageTable[page] & PF_WRITE) {
509                                 gaTmpTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW;
510                                 gaPageTable[page] = (gaPageTable[page] & ~PF_WRITE) | PF_COW;
511                                 INVLPG( page << 12 );
512                         }
513                         else
514                                 gaTmpTable[page] = gaPageTable[page];
515                 }
516         }
517         
518         // Map in kernel tables (and make fractal mapping)
519         for( i = 768; i < 1024; i ++ )
520         {
521                 // Fractal
522                 if( i == (PAGE_TABLE_ADDR >> 22) ) {
523                         gaTmpDir[ PAGE_TABLE_ADDR >> 22 ] = *gTmpCR3;
524                         continue;
525                 }
526                 
527                 if( gaPageDir[i] == 0 ) {
528                         gaTmpDir[i] = 0;
529                         continue;
530                 }
531                 
532                 //LOG("gaPageDir[%x/4] = 0x%x", i*4, gaPageDir[i]);
533                 MM_RefPhys( gaPageDir[i] & ~0xFFF );
534                 gaTmpDir[i] = gaPageDir[i];
535         }
536         
537         // Allocate kernel stack
538         for(i = KERNEL_STACKS >> 22;
539                 i < KERNEL_STACKS_END >> 22;
540                 i ++ )
541         {
542                 // Check if directory is allocated
543                 if( (gaPageDir[i] & 1) == 0 ) {
544                         gaTmpDir[i] = 0;
545                         continue;
546                 }               
547                 
548                 // We don't care about other kernel stacks, just the current one
549                 if( i != kStackBase >> 22 ) {
550                         MM_DerefPhys( gaPageDir[i] & ~0xFFF );
551                         gaTmpDir[i] = 0;
552                         continue;
553                 }
554                 
555                 // Create a copy
556                 gaTmpDir[i] = MM_AllocPhys() | 3;
557                 INVLPG( &gaTmpTable[i*1024] );
558                 for( j = 0; j < 1024; j ++ )
559                 {
560                         // Is the page allocated? If not, skip
561                         if( !(gaPageTable[i*1024+j] & 1) ) {
562                                 gaTmpTable[i*1024+j] = 0;
563                                 continue;
564                         }
565                         
566                         // We don't care about other kernel stacks
567                         if( ((i*1024+j)*4096 & ~(KERNEL_STACK_SIZE-1)) != kStackBase ) {
568                                 gaTmpTable[i*1024+j] = 0;
569                                 continue;
570                         }
571                         
572                         // Allocate page
573                         gaTmpTable[i*1024+j] = MM_AllocPhys() | 3;
574                         
575                         MM_RefPhys( gaTmpTable[i*1024+j] & ~0xFFF );
576                         
577                         tmp = (void *) MM_MapTemp( gaTmpTable[i*1024+j] & ~0xFFF );
578                         memcpy( tmp, (void *)( (i*1024+j)*0x1000 ), 0x1000 );
579                         MM_FreeTemp( (Uint)tmp );
580                 }
581         }
582         
583         ret = *gTmpCR3 & ~0xFFF;
584         RELEASE( &gilTempFractal );
585         
586         //LEAVE('x', ret);
587         return ret;
588 }
589
590 /**
591  * \fn tVAddr MM_NewKStack()
592  * \brief Create a new kernel stack
593  */
594 tVAddr MM_NewKStack()
595 {
596         tVAddr  base = KERNEL_STACKS;
597         Uint    i;
598         for(;base<KERNEL_STACKS_END;base+=KERNEL_STACK_SIZE)
599         {
600                 if(MM_GetPhysAddr(base) != 0)   continue;
601                 for(i=0;i<KERNEL_STACK_SIZE;i+=0x1000) {
602                         MM_Allocate(base+i);
603                 }
604                 return base+KERNEL_STACK_SIZE;
605         }
606         Warning("MM_NewKStack - No address space left\n");
607         return 0;
608 }
609
610 /**
611  * \fn tVAddr MM_NewWorkerStack()
612  * \brief Creates a new worker stack
613  */
614 tVAddr MM_NewWorkerStack()
615 {
616         Uint    esp, ebp;
617         Uint    oldstack;
618         Uint    base, addr;
619          int    i, j;
620         Uint    *tmpPage;
621         tPAddr  pages[WORKER_STACK_SIZE>>12];
622         
623         // Get the old ESP and EBP
624         __asm__ __volatile__ ("mov %%esp, %0": "=r"(esp));
625         __asm__ __volatile__ ("mov %%ebp, %0": "=r"(ebp));
626         
627         // Find a free worker stack address
628         for(base = giLastUsedWorker; base < NUM_WORKER_STACKS; base++)
629         {
630                 // Used block
631                 if( gWorkerStacks[base/32] == -1 ) {
632                         base += 31;     base &= ~31;
633                         base --;        // Counteracted by the base++
634                         continue;
635                 }
636                 // Used stack
637                 if( gWorkerStacks[base/32] & (1 << base) ) {
638                         continue;
639                 }
640                 break;
641         }
642         if(base >= NUM_WORKER_STACKS) {
643                 Warning("Uh-oh! Out of worker stacks");
644                 return 0;
645         }
646         
647         // It's ours now!
648         gWorkerStacks[base/32] |= (1 << base);
649         // Make life easier for later calls
650         giLastUsedWorker = base;
651         // We have one
652         base = WORKER_STACKS + base * WORKER_STACK_SIZE;
653         //Log(" MM_NewWorkerStack: base = 0x%x", base);
654         
655         // Acquire the lock for the temp fractal mappings
656         LOCK(&gilTempFractal);
657         
658         // Set the temp fractals to TID0's address space
659         *gTmpCR3 = ((Uint)gaInitPageDir - KERNEL_BASE) | 3;
660         //Log(" MM_NewWorkerStack: *gTmpCR3 = 0x%x", *gTmpCR3);
661         INVLPG( gaTmpDir );
662         
663         
664         // Check if the directory is mapped (we are assuming that the stacks
665         // will fit neatly in a directory)
666         //Log(" MM_NewWorkerStack: gaTmpDir[ 0x%x ] = 0x%x", base>>22, gaTmpDir[ base >> 22 ]);
667         if(gaTmpDir[ base >> 22 ] == 0) {
668                 gaTmpDir[ base >> 22 ] = MM_AllocPhys() | 3;
669                 INVLPG( &gaTmpTable[ (base>>12) & ~0x3FF ] );
670         }
671         
672         // Mapping Time!
673         for( addr = 0; addr < WORKER_STACK_SIZE; addr += 0x1000 )
674         {
675                 pages[ addr >> 12 ] = MM_AllocPhys();
676                 gaTmpTable[ (base + addr) >> 12 ] = pages[addr>>12] | 3;
677         }
678         *gTmpCR3 = 0;
679         // Release the temp mapping lock
680         RELEASE(&gilTempFractal);
681         
682         // Copy the old stack
683         oldstack = (esp + KERNEL_STACK_SIZE-1) & ~(KERNEL_STACK_SIZE-1);
684         esp = oldstack - esp;   // ESP as an offset in the stack
685         
686         // Make `base` be the top of the stack
687         base += WORKER_STACK_SIZE;
688         
689         i = (WORKER_STACK_SIZE>>12) - 1;
690         // Copy the contents of the old stack to the new one, altering the addresses
691         // `addr` is refering to bytes from the stack base (mem downwards)
692         for(addr = 0; addr < esp; addr += 0x1000)
693         {
694                 Uint    *stack = (Uint*)( oldstack-(addr+0x1000) );
695                 tmpPage = (void*)MM_MapTemp( pages[i] );
696                 // Copy old stack
697                 for(j = 0; j < 1024; j++)
698                 {
699                         // Possible Stack address?
700                         if(oldstack-esp < stack[j] && stack[j] < oldstack)
701                                 tmpPage[j] = base - (oldstack - stack[j]);
702                         else    // Seems not, best leave it alone
703                                 tmpPage[j] = stack[j];
704                 }
705                 MM_FreeTemp((tVAddr)tmpPage);
706                 i --;
707         }
708         
709         //Log("MM_NewWorkerStack: RETURN 0x%x", base);
710         return base;
711 }
712
713 /**
714  * \fn void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
715  * \brief Sets the flags on a page
716  */
717 void MM_SetFlags(tVAddr VAddr, Uint Flags, Uint Mask)
718 {
719         tTabEnt *ent;
720         if( !(gaPageDir[VAddr >> 22] & 1) )     return ;
721         if( !(gaPageTable[VAddr >> 12] & 1) )   return ;
722         
723         ent = &gaPageTable[VAddr >> 12];
724         
725         // Read-Only
726         if( Mask & MM_PFLAG_RO )
727         {
728                 if( Flags & MM_PFLAG_RO )       *ent &= ~PF_WRITE;
729                 else    *ent |= PF_WRITE;
730         }
731         
732         // Kernel
733         if( Mask & MM_PFLAG_KERNEL )
734         {
735                 if( Flags & MM_PFLAG_KERNEL )   *ent &= ~PF_USER;
736                 else    *ent |= PF_USER;
737         }
738         
739         // Copy-On-Write
740         if( Mask & MM_PFLAG_COW )
741         {
742                 if( Flags & MM_PFLAG_COW ) {
743                         *ent &= ~PF_WRITE;
744                         *ent |= PF_COW;
745                 }
746                 else {
747                         *ent &= ~PF_COW;
748                         *ent |= PF_WRITE;
749                 }
750         }
751 }
752
753 /**
754  * \fn tPAddr MM_DuplicatePage(tVAddr VAddr)
755  * \brief Duplicates a virtual page to a physical one
756  */
757 tPAddr MM_DuplicatePage(tVAddr VAddr)
758 {
759         tPAddr  ret;
760         Uint    temp;
761          int    wasRO = 0;
762         
763         // Check if mapped
764         if( !(gaPageDir  [VAddr >> 22] & PF_PRESENT) )  return 0;
765         if( !(gaPageTable[VAddr >> 12] & PF_PRESENT) )  return 0;
766         
767         // Page Align
768         VAddr &= ~0xFFF;
769         
770         // Allocate new page
771         ret = MM_AllocPhys();
772         
773         // Write-lock the page (to keep data constistent), saving its R/W state
774         wasRO = (gaPageTable[VAddr >> 12] & PF_WRITE ? 0 : 1);
775         gaPageTable[VAddr >> 12] &= ~PF_WRITE;
776         INVLPG( VAddr );
777         
778         // Copy Data
779         temp = MM_MapTemp(ret);
780         memcpy( (void*)temp, (void*)VAddr, 0x1000 );
781         MM_FreeTemp(temp);
782         
783         // Restore Writeable status
784         if(!wasRO)      gaPageTable[VAddr >> 12] |= PF_WRITE;
785         INVLPG(VAddr);
786         
787         return ret;
788 }
789
790 /**
791  * \fn Uint MM_MapTemp(tPAddr PAddr)
792  * \brief Create a temporary memory mapping
793  * \todo Show Luigi Barone (C Lecturer) and see what he thinks
794  */
795 tVAddr MM_MapTemp(tPAddr PAddr)
796 {
797          int    i;
798         
799         //ENTER("XPAddr", PAddr);
800         
801         PAddr &= ~0xFFF;
802         
803         //LOG("gilTempMappings = %i", gilTempMappings);
804         
805         for(;;)
806         {
807                 LOCK( &gilTempMappings );
808                 
809                 for( i = 0; i < NUM_TEMP_PAGES; i ++ )
810                 {
811                         // Check if page used
812                         if(gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] & 1)        continue;
813                         // Mark as used
814                         gaPageTable[ (TEMP_MAP_ADDR >> 12) + i ] = PAddr | 3;
815                         INVLPG( TEMP_MAP_ADDR + (i << 12) );
816                         //LEAVE('p', TEMP_MAP_ADDR + (i << 12));
817                         RELEASE( &gilTempMappings );
818                         return TEMP_MAP_ADDR + (i << 12);
819                 }
820                 RELEASE( &gilTempMappings );
821                 Threads_Yield();
822         }
823 }
824
825 /**
826  * \fn void MM_FreeTemp(tVAddr PAddr)
827  * \brief Free's a temp mapping
828  */
829 void MM_FreeTemp(tVAddr VAddr)
830 {
831          int    i = VAddr >> 12;
832         //ENTER("xVAddr", VAddr);
833         
834         if(i >= (TEMP_MAP_ADDR >> 12))
835                 gaPageTable[ i ] = 0;
836         
837         //LEAVE('-');
838 }
839
840 /**
841  * \fn tVAddr MM_MapHWPage(tPAddr PAddr, Uint Number)
842  * \brief Allocates a contigous number of pages
843  */
844 tVAddr MM_MapHWPage(tPAddr PAddr, Uint Number)
845 {
846          int    i, j;
847         
848         PAddr &= ~0xFFF;
849         
850         // Scan List
851         for( i = 0; i < NUM_HW_PAGES; i ++ )
852         {               
853                 // Check if addr used
854                 if( gaPageTable[ (HW_MAP_ADDR >> 12) + i ] & 1 )
855                         continue;
856                 
857                 // Check possible region
858                 for( j = 0; j < Number && i + j < NUM_HW_PAGES; j ++ )
859                 {
860                         // If there is an allocated page in the region we are testing, break
861                         if( gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] & 1 )    break;
862                 }
863                 // Is it all free?
864                 if( j == Number )
865                 {
866                         // Allocate
867                         for( j = 0; j < Number; j++ ) {
868                                 MM_RefPhys( PAddr + (j<<12) );
869                                 gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] = (PAddr + (j<<12)) | 3;
870                         }
871                         return HW_MAP_ADDR + (i<<12);
872                 }
873         }
874         // If we don't find any, return NULL
875         return 0;
876 }
877
878 /**
879  * \fn tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
880  * \brief Allocates DMA physical memory
881  * \param Pages Number of pages required
882  * \param MaxBits       Maximum number of bits the physical address can have
883  * \param PhysAddr      Pointer to the location to place the physical address allocated
884  * \return Virtual address allocate
885  */
886 tVAddr MM_AllocDMA(int Pages, int MaxBits, tPAddr *PhysAddr)
887 {
888         tPAddr  maxCheck = (1 << MaxBits);
889         tPAddr  phys;
890         tVAddr  ret;
891         
892         ENTER("iPages iMaxBits pPhysAddr", Pages, MaxBits, PhysAddr);
893         
894         // Sanity Check
895         if(MaxBits < 12 || !PhysAddr) {
896                 LEAVE('i', 0);
897                 return 0;
898         }
899         
900         // Bound
901         if(MaxBits >= PHYS_BITS)        maxCheck = -1;
902         
903         // Fast Allocate
904         if(Pages == 1 && MaxBits >= PHYS_BITS)
905         {
906                 phys = MM_AllocPhys();
907                 *PhysAddr = phys;
908                 ret = MM_MapHWPage(phys, 1);
909                 if(ret == 0) {
910                         MM_DerefPhys(phys);
911                         LEAVE('i', 0);
912                         return 0;
913                 }
914                 LEAVE('x', ret);
915                 return ret;
916         }
917         
918         // Slow Allocate
919         phys = MM_AllocPhysRange(Pages);
920         // - Was it allocated?
921         if(phys == 0) {
922                 LEAVE('i', 0);
923                 return 0;
924         }
925         // - Check if the memory is OK
926         if(phys + (Pages-1)*0x1000 > maxCheck)
927         {
928                 // Deallocate and return 0
929                 for(;Pages--;phys+=0x1000)
930                         MM_DerefPhys(phys);
931                 LEAVE('i', 0);
932                 return 0;
933         }
934         
935         // Allocated successfully, now map
936         ret = MM_MapHWPage(phys, Pages);
937         if( ret == 0 ) {
938                 // If it didn't map, free then return 0
939                 for(;Pages--;phys+=0x1000)
940                         MM_DerefPhys(phys);
941                 LEAVE('i', 0);
942                 return 0;
943         }
944         
945         *PhysAddr = phys;
946         LEAVE('x', ret);
947         return ret;
948 }
949
950 /**
951  * \fn void MM_UnmapHWPage(tVAddr VAddr, Uint Number)
952  * \brief Unmap a hardware page
953  */
954 void MM_UnmapHWPage(tVAddr VAddr, Uint Number)
955 {
956          int    i, j;
957         // Sanity Check
958         if(VAddr < HW_MAP_ADDR || VAddr-Number*0x1000 > HW_MAP_MAX)     return;
959         
960         i = VAddr >> 12;
961         
962         LOCK( &gilTempMappings );       // Temp and HW share a directory, so they share a lock
963         
964         for( j = 0; j < Number; j++ )
965         {
966                 MM_DerefPhys( gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] );
967                 gaPageTable[ (HW_MAP_ADDR >> 12) + i + j ] = 0;
968         }
969         
970         RELEASE( &gilTempMappings );
971 }
972
973 // --- EXPORTS ---
974 EXPORT(MM_GetPhysAddr);
975 EXPORT(MM_Map);
976 //EXPORT(MM_Unmap);
977 EXPORT(MM_MapHWPage);
978 EXPORT(MM_AllocDMA);
979 EXPORT(MM_UnmapHWPage);

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