3 * - Physical memory manager
10 #include <semaphore.h>
11 #include <debug_hooks.h>
14 #define TRACE_ALLOCS 0 // Print trace messages on AllocPhys/DerefPhys
16 static const int addrClasses[] = {0,16,20,24,32,64};
17 static const int numAddrClasses = sizeof(addrClasses)/sizeof(addrClasses[0]);
20 void MM_Install(int NPMemRanges, tPMemMapEnt *PMemRanges);
21 //tPAddr MM_AllocPhys(void);
22 //tPAddr MM_AllocPhysRange(int Pages, int MaxBits);
23 //void MM_RefPhys(tPAddr PAddr);
24 //void MM_DerefPhys(tPAddr PAddr);
25 // int MM_GetRefCount(tPAddr PAddr);
29 Uint64 giPhysAlloc = 0; // Number of allocated pages
30 Uint64 giPageCount = 0; // Total number of pages
31 Uint64 giLastPossibleFree = 0; // Last possible free page (before all pages are used)
32 Uint64 giTotalMemorySize = 0; // Total number of allocatable pages
34 Uint32 gaSuperBitmap[1024]; // Blocks of 1024 Pages
35 Uint32 gaPageBitmap[1024*1024/32]; // Individual pages
36 int *gaPageReferences;
37 void **gaPageNodes = (void*)MM_PAGENODE_BASE;
38 #define REFENT_PER_PAGE (0x1000/sizeof(gaPageReferences[0]))
41 void MM_Install(int NPMemRanges, tPMemMapEnt *PMemRanges)
45 // --- Find largest address
46 for( Uint i = 0; i < NPMemRanges; i ++ )
48 tPMemMapEnt *ent = &PMemRanges[i];
49 // If entry is RAM and is above `maxAddr`, change `maxAddr`
50 if(ent->Type == PMEMTYPE_FREE || ent->Type == PMEMTYPE_USED)
52 if(ent->Start + ent->Length > maxAddr)
53 maxAddr = ent->Start + ent->Length;
54 giTotalMemorySize += ent->Length >> 12;
57 LOG("giTotalMemorySize = %lli KiB", giTotalMemorySize*4);
58 LOG("maxAddr = 0x%X", maxAddr);
61 if( maxAddr > (1ULL << 32) ) {
62 maxAddr = (1ULL << 32);
65 giPageCount = maxAddr >> 12;
66 giLastPossibleFree = giPageCount - 1;
67 memsetd(gaPageBitmap, 0xFFFFFFFF, giPageCount/32);
69 // Set up allocateable space
70 for( Uint i = 0; i < NPMemRanges; i ++ )
72 tPMemMapEnt *ent = &PMemRanges[i];
73 if( ent->Type == PMEMTYPE_FREE )
75 Uint64 startpg = ent->Start / PAGE_SIZE;
76 Uint64 pgcount = ent->Length / PAGE_SIZE;
77 // Ignore start addresses >32 bits
78 if( startpg > (1 << 20) )
80 // Clip lengths to 32-bit address space
81 if( startpg + pgcount > (1<<20) )
82 pgcount = (1<<20) - startpg;
84 while( startpg % 32 && pgcount ) {
85 gaPageBitmap[startpg/32] &= ~(1U << (startpg%32));
89 memsetd( &gaPageBitmap[startpg/32], 0, pgcount/32 );
90 startpg += pgcount - pgcount%32;
91 pgcount -= pgcount - pgcount%32;
93 gaPageBitmap[startpg/32] &= ~(1U << (startpg%32));
98 else if( ent->Type == PMEMTYPE_USED )
101 giPhysAlloc += ent->Length / PAGE_SIZE;
105 // Fill Superpage bitmap
106 // - A set bit means that there are no free pages in this block of 32
107 for( Uint i = 0; i < (giPageCount+31)/32; i ++ )
109 if( gaPageBitmap[i] + 1 == 0 ) {
110 gaSuperBitmap[i/32] |= (1 << i%32);
114 gaPageReferences = (void*)MM_REFCOUNT_BASE;
116 Log_Debug("PMem", "maxAddr = %P", maxAddr);
117 Log_Log("PMem", "Physical memory set up (%lli pages of ~%lli MiB used)",
118 giPhysAlloc, (giTotalMemorySize*PAGE_SIZE)/(1024*1024)
122 void MM_DumpStatistics(void)
124 for( int i = 1; i < numAddrClasses; i ++ )
126 const int first = (i == 1 ? 0 : (1UL << (addrClasses[i-1] - 12)));
127 const int last = MIN( (1UL << (addrClasses[i] - 12)) - 1, giPageCount );
128 const int total = last - first + 1;
133 bool refpage_valid = !!MM_GetPhysAddr(&gaPageReferences[first]);
135 for( Uint pg = first; pg < last; pg ++ )
138 if( gaPageBitmap[pg/32] == 0 )
140 int count = 32 - pg%32;
147 if( !(gaPageBitmap[pg/32] & (1 << pg%32)) )
153 // Check if reference page is valid
154 if( pg % (PAGE_SIZE/sizeof(gaPageReferences[0])) == 0 ) {
155 refpage_valid = !!MM_GetPhysAddr(&gaPageReferences[pg]);
159 if( refpage_valid && gaPageReferences[pg] > 1 ) {
160 totalRefs += gaPageReferences[pg];
167 int nUsed = (total - nFree);
168 Log_Log("MMPhys", "%ipbit - %i/%i used, %i reused, %i average reference count",
169 addrClasses[i], nUsed, total, nMultiRef,
170 nMultiRef ? (totalRefs-(nUsed - nMultiRef)) / nMultiRef : 0
172 // TODO: Calculate fragentation of physical memory.
173 // > Somehow support defragmenting?
175 if( last == giPageCount )
178 Log_Log("MMPhys", "%lli/%lli total pages used, 0 - %i possible free range",
179 giPhysAlloc, giTotalMemorySize, giLastPossibleFree);
183 void *last_node = NULL;
184 for( int pg = 0; pg < giPageCount; pg ++ )
189 if( !(gaPageBitmap[pg/32] & (1 << pg%32)) )
196 refcount = MM_GetPhysAddr(&gaPageReferences[pg]) ? gaPageReferences[pg] : 1;
197 node = MM_GetPhysAddr(&gaPageNodes[pg]) ? gaPageNodes[pg] : NULL;
199 if( last_refcnt != refcount || last_node != node )
202 if( output || pg == giPageCount-1 )
204 if( last_refcnt > 0 )
205 Debug("0x%4x+%i: node=%p refcount=%i", pg-startpage, last_node, last_refcnt);
208 last_refcnt = refcount;
214 * \fn tPAddr MM_AllocPhys(void)
215 * \brief Allocates a physical page from the general pool
217 tPAddr MM_AllocPhys(void)
224 Mutex_Acquire( &glPhysAlloc );
230 for( i = numAddrClasses; i -- > 1; )
232 first = 1UL << (addrClasses[i-1] - 12);
233 last = (1UL << (addrClasses[i] - 12)) - 1;
234 // Range is above the last free page
235 if( first > giLastPossibleFree )
237 // Last possible free page is in the range
238 if( last > giLastPossibleFree )
239 last = giLastPossibleFree;
242 for( indx = first; indx < last; )
244 if( gaSuperBitmap[indx>>10] == -1 ) {
249 if( gaPageBitmap[indx>>5] == -1 ) {
254 if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) {
260 if( indx < last ) break;
262 giLastPossibleFree = first; // Well, we couldn't find any in this range
265 if( i <= 1 ) indx = -1;
269 Mutex_Release( &glPhysAlloc );
270 Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used (indx = %x)",
271 __builtin_return_address(0), giPhysAlloc, giPageCount, indx);
272 Log_Debug("PMem", "giLastPossibleFree = %lli", giLastPossibleFree);
277 if( indx > 0xFFFFF ) {
278 Panic("The fuck? Too many pages! (indx = 0x%x)", indx);
281 if( indx >= giPageCount ) {
282 Mutex_Release( &glPhysAlloc );
283 Log_Error("PMem", "MM_AllocPhys - indx(%i) > giPageCount(%i)", indx, giPageCount);
289 if( MM_GetPhysAddr( &gaPageReferences[indx] ) )
290 gaPageReferences[indx] = 1;
291 gaPageBitmap[ indx>>5 ] |= 1 << (indx&31);
299 if(gaPageBitmap[ indx>>5 ] == -1) {
300 gaSuperBitmap[indx>>10] |= 1 << ((indx>>5)&31);
304 Mutex_Release( &glPhysAlloc );
309 Log_Debug("PMem", "MM_AllocPhys: RETURN %P (%i free)", ret, giPageCount-giPhysAlloc);
310 Proc_PrintBacktrace();
317 * \fn tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
318 * \brief Allocate a range of physical pages
319 * \param Pages Number of pages to allocate
320 * \param MaxBits Maximum number of address bits to use
322 tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
327 ENTER("iPages iMaxBits", Pages, MaxBits);
334 if(MaxBits > PHYS_BITS) MaxBits = PHYS_BITS;
337 Mutex_Acquire( &glPhysAlloc );
339 // Set up search state
340 if( giLastPossibleFree > ((tPAddr)1 << (MaxBits-12)) ) {
341 sidx = (tPAddr)1 << (MaxBits-12);
344 sidx = giLastPossibleFree;
349 // Check if the gap is large enough
356 if( gaPageBitmap[idx] == -1 ) {
362 if( gaPageBitmap[idx] & (1 << sidx) ) {
364 if(sidx < 0) { sidx = 31; idx --; }
372 // Check if it is a free range
373 for( i = 0; i < Pages; i++ )
376 if( gaPageBitmap[idx] & (1 << sidx) )
380 if(sidx < 0) { sidx = 31; idx --; }
388 // Check if an address was found
390 Mutex_Release( &glPhysAlloc );
391 Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0));
397 for( i = 0; i < Pages; i++ )
399 if( MM_GetPhysAddr( &gaPageReferences[idx*32+sidx] ) )
400 gaPageReferences[idx*32+sidx] = 1;
401 gaPageBitmap[ idx ] |= 1 << sidx;
404 if(sidx == 32) { sidx = 0; idx ++; }
408 ret = (idx << 17) | (sidx << 12);
411 if(gaPageBitmap[ idx ] == -1) gaSuperBitmap[idx/32] |= 1 << (idx%32);
414 Mutex_Release( &glPhysAlloc );
418 Log_Debug("PMem", "MM_AllocPhysRange: RETURN 0x%llx-0x%llx (%i free)",
419 ret, ret + (1<<Pages)-1, giPageCount-giPhysAlloc);
425 * \fn void MM_RefPhys(tPAddr PAddr)
427 void MM_RefPhys(tPAddr PAddr)
432 //if( PAddr == 0x15FA000/PAGE_SIZE ) Debug("%p refed %P", __builtin_return_address(0), PAddr*PAGE_SIZE);
434 // We don't care about non-ram pages
435 if(PAddr >= giPageCount) return;
438 Mutex_Acquire( &glPhysAlloc );
440 // Reference the page
441 if( gaPageReferences )
443 if( MM_GetPhysAddr( &gaPageReferences[PAddr] ) == 0 )
445 Uint base = PAddr & ~(1024-1);
446 Mutex_Release( &glPhysAlloc );
447 // No infinite recursion, AllocPhys doesn't need the reference array
448 // TODO: Race condition? (racy on populating)
449 if( MM_Allocate( &gaPageReferences[base] ) == 0 )
451 Log_KernelPanic("PMem",
452 "MM_RefPhys: Out of physical memory allocating info for %X",
457 Mutex_Acquire( &glPhysAlloc );
458 // TODO: Solve race condition. (see below)
459 // [1] See unallocated
470 // Populate (clobbering)
472 // Fill references from allocated bitmap
473 for( int i = 0; i < 1024; i ++ )
475 gaPageReferences[base + i] = (gaPageBitmap[(base+i)/32] & (1 << (base+i)%32)) ? 1 : 0;
478 gaPageReferences[ PAddr ] ++;
481 // If not already used
482 if( !(gaPageBitmap[ PAddr / 32 ] & 1 << (PAddr&31)) ) {
485 gaPageBitmap[ PAddr / 32 ] |= 1 << (PAddr&31);
489 if(gaPageBitmap[ PAddr / 32 ] == -1)
490 gaSuperBitmap[PAddr/1024] |= 1 << ((PAddr/32)&31);
493 Mutex_Release( &glPhysAlloc );
497 * \fn void MM_DerefPhys(tPAddr PAddr)
498 * \brief Dereferences a physical page
500 void MM_DerefPhys(tPAddr PAddr)
505 //if( PAddr == 0x196000/PAGE_SIZE ) Debug("%p derefed %P", __builtin_return_address(0), PAddr*PAGE_SIZE);
507 // We don't care about non-ram pages
508 if(PAddr >= giPageCount) return;
510 // Check if it is freed
511 if( !(gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ) {
512 Log_Warning("MMVirt", "MM_DerefPhys - Non-referenced memory (%P) dereferenced",
514 Proc_PrintBacktrace();
519 Mutex_Acquire( &glPhysAlloc );
521 if( giLastPossibleFree < PAddr )
522 giLastPossibleFree = PAddr;
525 if( !MM_GetPhysAddr( &gaPageReferences[PAddr] ) || (-- gaPageReferences[PAddr]) == 0 )
528 Log_Debug("PMem", "MM_DerefPhys: Free'd %P (%i free)", PAddr<<12, giPageCount-giPhysAlloc);
529 Proc_PrintBacktrace();
531 //LOG("Freed 0x%x by %p\n", PAddr<<12, __builtin_return_address(0));
533 gaPageBitmap[ PAddr / 32 ] &= ~(1 << (PAddr&31));
534 if(gaPageBitmap[ PAddr / 32 ] == 0)
535 gaSuperBitmap[ PAddr >> 10 ] &= ~(1 << ((PAddr >> 5)&31));
537 if( MM_GetPhysAddr( &gaPageNodes[PAddr] ) )
539 gaPageNodes[PAddr] = NULL;
540 // TODO: Free Node Page when fully unused
545 Mutex_Release( &glPhysAlloc );
549 * \fn int MM_GetRefCount(tPAddr Addr)
551 int MM_GetRefCount(tPAddr PAddr)
556 // We don't care about non-ram pages
557 if(PAddr >= giPageCount) return -1;
559 if( MM_GetPhysAddr( &gaPageReferences[PAddr] ) == 0 )
560 return (gaPageBitmap[PAddr / 32] & (1 << PAddr%32)) ? 1 : 0;
562 // Check if it is freed
563 return gaPageReferences[ PAddr ];
566 int MM_SetPageNode(tPAddr PAddr, void *Node)
568 if( MM_GetRefCount(PAddr) == 0 ) return 1;
572 void *page_ptr = (void*)( (tVAddr)&gaPageNodes[PAddr] & ~(PAGE_SIZE-1) );
574 if( !MM_GetPhysAddr( page_ptr ) )
576 if( !MM_Allocate( page_ptr ) ) {
577 Log_Warning("PMem", "Unable to allocate Node page");
580 memset( page_ptr, 0, PAGE_SIZE );
583 gaPageNodes[PAddr] = Node;
584 // Log("gaPageNodes[0x%x] = %p", PAddr, Node);
588 int MM_GetPageNode(tPAddr PAddr, void **Node)
590 if( MM_GetRefCount(PAddr) == 0 ) return 1;
593 if( !MM_GetPhysAddr( &gaPageNodes[PAddr] ) ) {
597 *Node = gaPageNodes[PAddr];