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

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