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

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