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

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