557681164cccb564ebc3fa05284d472e6a0ab860
[tpg/acess2.git] / KernelLand / Kernel / arch / x86 / mm_phys.c
1 /*
2  * Acess2
3  * - Physical memory manager
4  */
5 #define DEBUG   0
6 #include <acess.h>
7 #include <mboot.h>
8 #include <mm_virt.h>
9
10 //#define USE_STACK     1
11 #define TRACE_ALLOCS    0       // Print trace messages on AllocPhys/DerefPhys
12
13
14 // === IMPORTS ===
15 extern char     gKernelEnd[];
16 extern void     Proc_PrintBacktrace(void);
17
18 // === PROTOTYPES ===
19 void    MM_Install(tMBoot_Info *MBoot);
20 //tPAddr        MM_AllocPhys(void);
21 //tPAddr        MM_AllocPhysRange(int Pages, int MaxBits);
22 //void  MM_RefPhys(tPAddr PAddr);
23 //void  MM_DerefPhys(tPAddr PAddr);
24 // int  MM_GetRefCount(tPAddr PAddr);
25
26 // === GLOBALS ===
27 tMutex  glPhysAlloc;
28 Uint64  giPhysAlloc = 0;        // Number of allocated pages
29 Uint64  giPageCount = 0;        // Total number of pages
30 Uint64  giLastPossibleFree = 0; // Last possible free page (before all pages are used)
31
32 Uint32  gaSuperBitmap[1024];    // Blocks of 1024 Pages
33 Uint32  gaPageBitmap[1024*1024/32];     // Individual pages
34  int    *gaPageReferences;
35 void    **gaPageNodes = (void*)MM_PAGENODE_BASE;
36 #define REFENT_PER_PAGE (0x1000/sizeof(gaPageReferences[0]))
37
38 // === CODE ===
39 void MM_Install(tMBoot_Info *MBoot)
40 {
41         Uint    kernelPages, num;
42         Uint    i;
43         Uint64  maxAddr = 0;
44         tMBoot_Module   *mods;
45         tMBoot_MMapEnt  *ent;
46         
47         // --- Find largest address
48         MBoot->MMapAddr |= KERNEL_BASE;
49         ent = (void *)( MBoot->MMapAddr );
50         while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength )
51         {
52                 // Adjust for size
53                 ent->Size += 4;
54                 
55                 // If entry is RAM and is above `maxAddr`, change `maxAddr`
56                 if(ent->Type == 1 && ent->Base + ent->Length > maxAddr)
57                         maxAddr = ent->Base + ent->Length;
58                 // Go to next entry
59                 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
60         }
61         
62         if(maxAddr == 0) {      
63                 giPageCount = (MBoot->HighMem >> 2) + 256;      // HighMem is a kByte value
64         }
65         else {
66                 giPageCount = maxAddr >> 12;
67         }
68         giLastPossibleFree = giPageCount - 1;
69         
70         memsetd(gaPageBitmap, 0xFFFFFFFF, giPageCount/32);
71         
72         // Set up allocateable space
73         ent = (void *)( MBoot->MMapAddr );
74         while( (Uint)ent < MBoot->MMapAddr + MBoot->MMapLength )
75         {               
76                 memsetd( &gaPageBitmap[ent->Base/(4096*32)], 0, ent->Length/(4096*32) );
77                 ent = (tMBoot_MMapEnt *)( (Uint)ent + ent->Size );
78         }
79         
80         // Get used page count (Kernel)
81         kernelPages = (Uint)&gKernelEnd - KERNEL_BASE - 0x100000;
82         kernelPages += 0xFFF;   // Page Align
83         kernelPages >>= 12;
84         giPhysAlloc += kernelPages;     // Add to used count    
85
86         // Fill page bitmap
87         num = kernelPages/32;
88         memsetd( &gaPageBitmap[0x100000/(4096*32)], -1, num );
89         gaPageBitmap[ 0x100000/(4096*32) + num ] = (1 << (kernelPages & 31)) - 1;
90         
91         // Fill Superpage bitmap
92         num = kernelPages/(32*32);
93         memsetd( &gaSuperBitmap[0x100000/(4096*32*32)], -1, num );
94         gaSuperBitmap[ 0x100000/(4096*32*32) + num ] = (1 << ((kernelPages / 32) & 31)) - 1;
95         
96         // Mark Multiboot's pages as taken
97         // - Structure
98         MM_RefPhys( (Uint)MBoot - KERNEL_BASE );
99         // - Module List
100         for(i = (MBoot->ModuleCount*sizeof(tMBoot_Module)+0xFFF)>12; i--; )
101                 MM_RefPhys( MBoot->Modules + (i << 12) );
102         // - Modules
103         mods = (void*)(MBoot->Modules + KERNEL_BASE);
104         for(i = 0; i < MBoot->ModuleCount; i++)
105         {
106                 num = (mods[i].End - mods[i].Start + 0xFFF) >> 12;
107                 while(num--)
108                         MM_RefPhys( (mods[i].Start & ~0xFFF) + (num<<12) );
109         }
110
111         gaPageReferences = (void*)MM_REFCOUNT_BASE;
112
113         Log_Log("PMem", "Physical memory set up (%lli pages of ~%lli MiB used)",
114                 giPhysAlloc, (giPageCount*4)/1024
115                 );
116 }
117
118 /**
119  * \fn tPAddr MM_AllocPhys(void)
120  * \brief Allocates a physical page from the general pool
121  */
122 tPAddr MM_AllocPhys(void)
123 {
124         // int  a, b, c;
125          int    indx = -1;
126         tPAddr  ret;
127         
128         ENTER("");
129         
130         Mutex_Acquire( &glPhysAlloc );
131         
132         // Classful scan
133         #if 1
134         {
135         const int addrClasses[] = {0,16,20,24,32,64};
136         const int numAddrClasses = sizeof(addrClasses)/sizeof(addrClasses[0]);
137          int    i;
138          int    first, last;
139         for( i = numAddrClasses; i -- > 1; )
140         {
141                 first = 1UL << (addrClasses[i-1] - 12);
142                 last = (1UL << (addrClasses[i] - 12)) - 1;
143                 // Range is above the last free page
144                 if( first > giLastPossibleFree )
145                         continue;
146                 // Last possible free page is in the range
147                 if( last > giLastPossibleFree )
148                         last = giLastPossibleFree;
149                         
150                 // Scan the range
151                 for( indx = first; indx < last; )
152                 {
153                         if( gaSuperBitmap[indx>>10] == -1 ) {
154                                 indx += 1024;
155                                 continue;
156                         }
157                         
158                         if( gaPageBitmap[indx>>5] == -1 ) {
159                                 indx += 32;
160                                 continue;
161                         }
162                         
163                         if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) {
164                                 indx ++;
165                                 continue;
166                         }
167                         break;
168                 }
169                 if( indx < last )       break;
170                 
171                 giLastPossibleFree = first;     // Well, we couldn't find any in this range
172         }
173         // Out of memory?
174         if( i <= 1 )    indx = -1;
175         }
176         #elif 0
177         // Find free page
178         // Scan downwards
179         LOG("giLastPossibleFree = %i", giLastPossibleFree);
180         for( indx = giLastPossibleFree; indx >= 0; )
181         {
182                 if( gaSuperBitmap[indx>>10] == -1 ) {
183                         indx -= 1024;
184                         continue;
185                 }
186                 
187                 if( gaPageBitmap[indx>>5] == -1 ) {
188                         indx -= 32;
189                         continue;
190                 }
191                 
192                 if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) {
193                         indx --;
194                         continue;
195                 }
196                 break;
197         }
198         if( indx >= 0 )
199                 giLastPossibleFree = indx;
200         LOG("indx = %i", indx);
201         #else
202         c = giLastPossibleFree % 32;
203         b = (giLastPossibleFree / 32) % 32;
204         a = giLastPossibleFree / 1024;
205         
206         LOG("a=%i,b=%i,c=%i", a, b, c);
207         for( ; gaSuperBitmap[a] == -1 && a >= 0; a-- );
208         if(a < 0) {
209                 Mutex_Release( &glPhysAlloc );
210                 Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used",
211                         __builtin_return_address(0), giPhysAlloc, giPageCount);
212                 LEAVE('i', 0);
213                 return 0;
214         }
215         for( ; gaSuperBitmap[a] & (1<<b); b-- );
216         for( ; gaPageBitmap[a*32+b] & (1<<c); c-- );
217         LOG("a=%i,b=%i,c=%i", a, b, c);
218         indx = (a << 10) | (b << 5) | c;
219         if( indx >= 0 )
220                 giLastPossibleFree = indx;
221         #endif
222         
223         if( indx < 0 ) {
224                 Mutex_Release( &glPhysAlloc );
225                 Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used (indx = %x)",
226                         __builtin_return_address(0), giPhysAlloc, giPageCount, indx);
227                 Log_Debug("PMem", "giLastPossibleFree = %lli", giLastPossibleFree);
228                 LEAVE('i', 0);
229                 return 0;
230         }
231         
232         if( indx > 0xFFFFF ) {
233                 Panic("The fuck? Too many pages! (indx = 0x%x)", indx);
234         }
235         
236         if( indx >= giPageCount ) {
237                 Mutex_Release( &glPhysAlloc );
238                 Log_Error("PMem", "MM_AllocPhys - indx(%i) > giPageCount(%i)", indx, giPageCount);
239                 LEAVE('i', 0);
240                 return 0;
241         }
242         
243         // Mark page used
244         if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[indx] ) )
245                 gaPageReferences[indx] = 1;
246         gaPageBitmap[ indx>>5 ] |= 1 << (indx&31);
247         
248         giPhysAlloc ++;
249         
250         // Get address
251         ret = indx << 12;
252         
253         // Mark used block
254         if(gaPageBitmap[ indx>>5 ] == -1) {
255                 gaSuperBitmap[indx>>10] |= 1 << ((indx>>5)&31);
256         }
257
258         // Release Spinlock
259         Mutex_Release( &glPhysAlloc );
260         
261         LEAVE('X', ret);
262         #if TRACE_ALLOCS
263         if( now() > 4000 ) {
264         Log_Debug("PMem", "MM_AllocPhys: RETURN %P (%i free)", ret, giPageCount-giPhysAlloc);
265         Proc_PrintBacktrace();
266         }
267         #endif
268         return ret;
269 }
270
271 /**
272  * \fn tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
273  * \brief Allocate a range of physical pages
274  * \param Pages Number of pages to allocate
275  * \param MaxBits       Maximum number of address bits to use
276  */
277 tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
278 {
279          int    a, b;
280          int    i, idx, sidx;
281         tPAddr  ret;
282         
283         ENTER("iPages iMaxBits", Pages, MaxBits);
284         
285         // Sanity Checks
286         if(MaxBits < 0) {
287                 LEAVE('i', 0);
288                 return 0;
289         }
290         if(MaxBits > PHYS_BITS) MaxBits = PHYS_BITS;
291         
292         // Lock
293         Mutex_Acquire( &glPhysAlloc );
294         
295         // Set up search state
296         if( giLastPossibleFree > ((tPAddr)1 << (MaxBits-12)) ) {
297                 sidx = (tPAddr)1 << (MaxBits-12);
298         }
299         else {
300                 sidx = giLastPossibleFree;
301         }
302         idx = sidx / 32;
303         sidx %= 32;
304         b = idx % 32;
305         a = idx / 32;
306         
307         #if 0
308         LOG("a=%i, b=%i, idx=%i, sidx=%i", a, b, idx, sidx);
309         
310         // Find free page
311         for( ; gaSuperBitmap[a] == -1 && a --; )        b = 31;
312         if(a < 0) {
313                 Mutex_Release( &glPhysAlloc );
314                 Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0));
315                 LEAVE('i', 0);
316                 return 0;
317         }
318         LOG("a = %i", a);
319         for( ; gaSuperBitmap[a] & (1 << b); b-- )       sidx = 31;
320         LOG("b = %i", b);
321         idx = a * 32 + b;
322         for( ; gaPageBitmap[idx] & (1 << sidx); sidx-- )
323                 LOG("gaPageBitmap[%i] = 0x%08x", idx, gaPageBitmap[idx]);
324         
325         LOG("idx = %i, sidx = %i", idx, sidx);
326         #else
327         
328         #endif
329         
330         // Check if the gap is large enough
331         while( idx >= 0 )
332         {
333                 // Find a free page
334                 for( ; ; )
335                 {
336                         // Bulk Skip
337                         if( gaPageBitmap[idx] == -1 ) {
338                                 idx --;
339                                 sidx = 31;
340                                 continue;
341                         }
342                         
343                         if( gaPageBitmap[idx] & (1 << sidx) ) {
344                                 sidx --;
345                                 if(sidx < 0) {  sidx = 31;      idx --; }
346                                 if(idx < 0)     break;
347                                 continue;
348                         }
349                         break;
350                 }
351                 if( idx < 0 )   break;
352                 
353                 // Check if it is a free range
354                 for( i = 0; i < Pages; i++ )
355                 {
356                         // Used page? break
357                         if( gaPageBitmap[idx] & (1 << sidx) )
358                                 break;
359                         
360                         sidx --;
361                         if(sidx < 0) {  sidx = 31;      idx --; }
362                         if(idx < 0)     break;
363                 }
364                 
365                 if( i == Pages )
366                         break;
367         }
368         
369         // Check if an address was found
370         if( idx < 0 ) {
371                 Mutex_Release( &glPhysAlloc );
372                 Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0));
373                 LEAVE('i', 0);
374                 return 0;
375         }
376         
377         // Mark pages used
378         for( i = 0; i < Pages; i++ )
379         {
380                 if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[idx*32+sidx] ) )
381                         gaPageReferences[idx*32+sidx] = 1;
382                 gaPageBitmap[ idx ] |= 1 << sidx;
383                 sidx ++;
384                 giPhysAlloc ++;
385                 if(sidx == 32) { sidx = 0;      idx ++; }
386         }
387         
388         // Get address
389         ret = (idx << 17) | (sidx << 12);
390         
391         // Mark used block
392         if(gaPageBitmap[ idx ] == -1)   gaSuperBitmap[idx/32] |= 1 << (idx%32);
393
394         // Release Spinlock
395         Mutex_Release( &glPhysAlloc );
396         
397         LEAVE('X', ret);
398         #if TRACE_ALLOCS
399         Log_Debug("PMem", "MM_AllocPhysRange: RETURN 0x%llx-0x%llx (%i free)",
400                 ret, ret + (1<<Pages)-1, giPageCount-giPhysAlloc);
401         #endif
402         return ret;
403 }
404
405 /**
406  * \fn void MM_RefPhys(tPAddr PAddr)
407  */
408 void MM_RefPhys(tPAddr PAddr)
409 {
410         // Get page number
411         PAddr >>= 12;
412
413         // We don't care about non-ram pages
414         if(PAddr >= giPageCount)        return;
415         
416         // Lock Structures
417         Mutex_Acquire( &glPhysAlloc );
418         
419         // Reference the page
420         if( gaPageReferences )
421         {
422                 if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) == 0 )
423                 {
424                          int    i, base;
425                         tVAddr  addr = ((tVAddr)&gaPageReferences[PAddr]) & ~0xFFF;
426 //                      Log_Debug("PMem", "MM_RefPhys: Allocating info for %X", PAddr);
427                         Mutex_Release( &glPhysAlloc );
428                         if( MM_Allocate( addr ) == 0 ) {
429                                 Log_KernelPanic("PMem",
430                                         "MM_RefPhys: Out of physical memory allocating info for %X",
431                                         PAddr*PAGE_SIZE
432                                         );
433                         }
434                         Mutex_Acquire( &glPhysAlloc );
435                         
436                         base = PAddr & ~(1024-1);
437                         for( i = 0; i < 1024; i ++ ) {
438                                 gaPageReferences[base + i] = (gaPageBitmap[(base+i)/32] & (1 << (base+i)%32)) ? 1 : 0;
439                         }
440                 }
441                 gaPageReferences[ PAddr ] ++;
442         }
443
444         // If not already used
445         if( !(gaPageBitmap[ PAddr / 32 ] & 1 << (PAddr&31)) ) {
446                 giPhysAlloc ++;
447                 // Mark as used
448                 gaPageBitmap[ PAddr / 32 ] |= 1 << (PAddr&31);
449         }
450         
451         // Mark used block
452         if(gaPageBitmap[ PAddr / 32 ] == -1)
453                 gaSuperBitmap[PAddr/1024] |= 1 << ((PAddr/32)&31);
454         
455         // Release Spinlock
456         Mutex_Release( &glPhysAlloc );
457 }
458
459 /**
460  * \fn void MM_DerefPhys(tPAddr PAddr)
461  * \brief Dereferences a physical page
462  */
463 void MM_DerefPhys(tPAddr PAddr)
464 {
465         // Get page number
466         PAddr >>= 12;
467
468         // We don't care about non-ram pages
469         if(PAddr >= giPageCount)        return;
470         
471         // Check if it is freed
472         if( !(gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ) {
473                 Log_Warning("MMVirt", "MM_DerefPhys - Non-referenced memory dereferenced");
474                 return;
475         }
476         
477         // Lock Structures
478         Mutex_Acquire( &glPhysAlloc );
479         
480         if( giLastPossibleFree < PAddr )
481                 giLastPossibleFree = PAddr;
482
483         // Dereference
484         if( !MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) || (-- gaPageReferences[PAddr]) == 0 )
485         {
486                 #if TRACE_ALLOCS
487                 Log_Debug("PMem", "MM_DerefPhys: Free'd %P (%i free)", PAddr<<12, giPageCount-giPhysAlloc);
488                 Proc_PrintBacktrace();
489                 #endif
490                 //LOG("Freed 0x%x by %p\n", PAddr<<12, __builtin_return_address(0));
491                 giPhysAlloc --;
492                 gaPageBitmap[ PAddr / 32 ] &= ~(1 << (PAddr&31));
493                 if(gaPageBitmap[ PAddr / 32 ] == 0)
494                         gaSuperBitmap[ PAddr >> 10 ] &= ~(1 << ((PAddr >> 5)&31));
495
496                 if( MM_GetPhysAddr( (tVAddr) &gaPageNodes[PAddr] ) )
497                 {
498                         gaPageNodes[PAddr] = NULL;
499                         // TODO: Free Node Page when fully unused
500                 }
501         }
502
503         // Release spinlock
504         Mutex_Release( &glPhysAlloc );
505 }
506
507 /**
508  * \fn int MM_GetRefCount(tPAddr Addr)
509  */
510 int MM_GetRefCount(tPAddr PAddr)
511 {
512         // Get page number
513         PAddr >>= 12;
514         
515         // We don't care about non-ram pages
516         if(PAddr >= giPageCount)        return -1;
517
518         if( MM_GetPhysAddr( (tVAddr)&gaPageReferences[PAddr] ) == 0 )
519                 return (gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ? 1 : 0;
520         
521         // Check if it is freed
522         return gaPageReferences[ PAddr ];
523 }
524
525 int MM_SetPageNode(tPAddr PAddr, void *Node)
526 {
527         tVAddr  block_addr;
528         
529         if( MM_GetRefCount(PAddr) == 0 )        return 1;
530          
531         PAddr /= PAGE_SIZE;
532
533         block_addr = (tVAddr) &gaPageNodes[PAddr];
534         block_addr &= ~(PAGE_SIZE-1);
535         
536         if( !MM_GetPhysAddr( block_addr ) )
537         {
538                 if( !MM_Allocate( block_addr ) ) {
539                         Log_Warning("PMem", "Unable to allocate Node page");
540                         return -1;
541                 }
542                 memset( (void*)block_addr, 0, PAGE_SIZE );
543         }
544
545         gaPageNodes[PAddr] = Node;
546 //      Log("gaPageNodes[0x%x] = %p", PAddr, Node);
547         return 0;
548 }
549
550 int MM_GetPageNode(tPAddr PAddr, void **Node)
551 {
552         if( MM_GetRefCount(PAddr) == 0 )        return 1;
553         
554         PAddr /= PAGE_SIZE;
555         if( !MM_GetPhysAddr( (tVAddr) &gaPageNodes[PAddr] ) ) {
556                 *Node = NULL;
557                 return 0;
558         }
559         *Node = gaPageNodes[PAddr];
560         return 0;
561 }
562

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