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

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