Kernel - x86 Fixed a couple of bugs
[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 #define REFERENCE_BASE  0xE0400000
14
15 // === IMPORTS ===
16 extern void     gKernelEnd;
17
18 // === PROTOTYPES ===
19 void    MM_Install(tMBoot_Info *MBoot);
20 //tPAddr        MM_AllocPhys(void);
21 //tPAddr        MM_AllocPhysRange(int Pages, int MaxBits);
22 //void  MM_RefPhys(tPAddr PAddr);
23 //void  MM_DerefPhys(tPAddr PAddr);
24 // int  MM_GetRefCount(tPAddr PAddr);
25
26 // === GLOBALS ===
27 tMutex  glPhysAlloc;
28 Uint64  giPhysAlloc = 0;        // Number of allocated pages
29 Uint64  giPageCount = 0;        // Total number of pages
30 Uint64  giLastPossibleFree = 0; // Last possible free page (before all pages are used)
31
32 Uint32  gaSuperBitmap[1024];    // Blocks of 1024 Pages
33 Uint32  gaPageBitmap[1024*1024/32];     // Individual pages
34 Uint32  *gaPageReferences;
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         // Allocate References
109         //LOG("Reference Pages %i", (giPageCount*4+0xFFF)>>12);
110         for(num = 0; num < (giPageCount*4+0xFFF)>>12; num++)
111         {
112                 if( !MM_Allocate( REFERENCE_BASE + (num<<12) ) )
113                 {
114                         Panic("Oh, ****, no space for the reference pages, that's bad");
115                         for(;;);
116                 }
117         }
118         
119         //LOG("Filling");
120         // Fill references
121         gaPageReferences = (void*)REFERENCE_BASE;
122         memsetd(gaPageReferences, 1, kernelPages);
123         for( num = kernelPages; num < giPageCount; num++ )
124         {
125                 gaPageReferences[num] = (gaPageBitmap[ num / 32 ] >> (num&31)) & 1;
126         }
127 }
128
129 /**
130  * \fn tPAddr MM_AllocPhys(void)
131  * \brief Allocates a physical page from the general pool
132  */
133 tPAddr MM_AllocPhys(void)
134 {
135         // int  a, b, c;
136          int    indx = -1;
137         tPAddr  ret;
138         
139         ENTER("");
140         
141         Mutex_Acquire( &glPhysAlloc );
142         
143         // Classful scan
144         #if 1
145         {
146         const int addrClasses[] = {0,16,20,24,32,64};
147         const int numAddrClasses = sizeof(addrClasses)/sizeof(addrClasses[0]);
148          int    i;
149          int    first, last;
150         for( i = numAddrClasses; i -- > 1; )
151         {
152 //              Log("Scanning %i (%i bits)", i, addrClasses[i]);
153                 first = 1 << (addrClasses[i-1] - 12);
154                 last = (1 << (addrClasses[i] - 12)) - 1;
155                 // Range is above the last free page
156                 if( first > giLastPossibleFree )
157                         continue;
158                 // Last possible free page is in the range
159                 if( last > giLastPossibleFree )
160                         last = giLastPossibleFree;
161                         
162 //              Log(" first=%i,max=%i", first, last);
163                 // Scan the range
164                 for( indx = first; indx < last; )
165                 {
166 //                      Log("indx = %i (< %i?)", indx, last);
167                         if( gaSuperBitmap[indx>>10] == -1 ) {
168                                 indx += 1024;
169                                 continue;
170                         }
171                         
172                         if( gaPageBitmap[indx>>5] == -1 ) {
173                                 indx += 32;
174                                 continue;
175                         }
176                         
177                         if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) {
178                                 indx ++;
179                                 continue;
180                         }
181                         break;
182                 }
183                 if( indx < last )       break;
184                 
185                 giLastPossibleFree = first;     // Well, we couldn't find any in this range
186         }
187         // Out of memory?
188         if( i <= 1 )    indx = -1;
189 //      Log("indx = %i", indx);
190         }
191         #elif 0
192         // Find free page
193         // Scan downwards
194         LOG("giLastPossibleFree = %i", giLastPossibleFree);
195         for( indx = giLastPossibleFree; indx >= 0; )
196         {
197                 if( gaSuperBitmap[indx>>10] == -1 ) {
198                         indx -= 1024;
199                         continue;
200                 }
201                 
202                 if( gaPageBitmap[indx>>5] == -1 ) {
203                         indx -= 32;
204                         continue;
205                 }
206                 
207                 if( gaPageBitmap[indx>>5] & (1 << (indx&31)) ) {
208                         indx --;
209                         continue;
210                 }
211                 break;
212         }
213         if( indx >= 0 )
214                 giLastPossibleFree = indx;
215         LOG("indx = %i", indx);
216         #else
217         c = giLastPossibleFree % 32;
218         b = (giLastPossibleFree / 32) % 32;
219         a = giLastPossibleFree / 1024;
220         
221         LOG("a=%i,b=%i,c=%i", a, b, c);
222         for( ; gaSuperBitmap[a] == -1 && a >= 0; a-- );
223         if(a < 0) {
224                 Mutex_Release( &glPhysAlloc );
225                 Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used",
226                         __builtin_return_address(0), giPhysAlloc, giPageCount);
227                 LEAVE('i', 0);
228                 return 0;
229         }
230         for( ; gaSuperBitmap[a] & (1<<b); b-- );
231         for( ; gaPageBitmap[a*32+b] & (1<<c); c-- );
232         LOG("a=%i,b=%i,c=%i", a, b, c);
233         indx = (a << 10) | (b << 5) | c;
234         if( indx >= 0 )
235                 giLastPossibleFree = indx;
236         #endif
237         
238         if( indx < 0 ) {
239                 Mutex_Release( &glPhysAlloc );
240                 Warning("MM_AllocPhys - OUT OF MEMORY (Called by %p) - %lli/%lli used (indx = %x)",
241                         __builtin_return_address(0), giPhysAlloc, giPageCount, indx);
242                 Log_Debug("PMem", "giLastPossibleFree = %lli", giLastPossibleFree);
243                 LEAVE('i', 0);
244                 return 0;
245         }
246         
247         if( indx > 0xFFFFF ) {
248                 Panic("The fuck? Too many pages! (indx = 0x%x)", indx);
249         }
250         
251         if( indx >= giPageCount ) {
252                 Mutex_Release( &glPhysAlloc );
253                 Log_Error("PMem", "MM_AllocPhys - indx(%i) > giPageCount(%i)", indx, giPageCount);
254                 LEAVE('i', 0);
255                 return 0;
256         }
257         
258         // Mark page used
259         if(gaPageReferences)
260                 gaPageReferences[ indx ] = 1;
261         gaPageBitmap[ indx>>5 ] |= 1 << (indx&31);
262         
263         giPhysAlloc ++;
264         
265         // Get address
266         ret = indx << 12;
267         
268         // Mark used block
269         if(gaPageBitmap[ indx>>5 ] == -1) {
270                 gaSuperBitmap[indx>>10] |= 1 << ((indx>>5)&31);
271         }
272
273         // Release Spinlock
274         Mutex_Release( &glPhysAlloc );
275         
276         LEAVE('X', ret);
277         #if TRACE_ALLOCS
278         Log_Debug("PMem", "MM_AllocPhys: RETURN 0x%llx (%i free)", ret, giPageCount-giPhysAlloc);
279         #endif
280         return ret;
281 }
282
283 /**
284  * \fn tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
285  * \brief Allocate a range of physical pages
286  * \param Pages Number of pages to allocate
287  * \param MaxBits       Maximum number of address bits to use
288  */
289 tPAddr MM_AllocPhysRange(int Pages, int MaxBits)
290 {
291          int    a, b;
292          int    i, idx, sidx;
293         tPAddr  ret;
294         
295         ENTER("iPages iMaxBits", Pages, MaxBits);
296         
297         // Sanity Checks
298         if(MaxBits < 0) {
299                 LEAVE('i', 0);
300                 return 0;
301         }
302         if(MaxBits > PHYS_BITS) MaxBits = PHYS_BITS;
303         
304         // Lock
305         Mutex_Acquire( &glPhysAlloc );
306         
307         // Set up search state
308         if( giLastPossibleFree > ((tPAddr)1 << (MaxBits-12)) ) {
309                 sidx = (tPAddr)1 << (MaxBits-12);
310         }
311         else {
312                 sidx = giLastPossibleFree;
313         }
314         idx = sidx / 32;
315         sidx %= 32;
316         b = idx % 32;
317         a = idx / 32;
318         
319         #if 0
320         LOG("a=%i, b=%i, idx=%i, sidx=%i", a, b, idx, sidx);
321         
322         // Find free page
323         for( ; gaSuperBitmap[a] == -1 && a --; )        b = 31;
324         if(a < 0) {
325                 Mutex_Release( &glPhysAlloc );
326                 Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0));
327                 LEAVE('i', 0);
328                 return 0;
329         }
330         LOG("a = %i", a);
331         for( ; gaSuperBitmap[a] & (1 << b); b-- )       sidx = 31;
332         LOG("b = %i", b);
333         idx = a * 32 + b;
334         for( ; gaPageBitmap[idx] & (1 << sidx); sidx-- )
335                 LOG("gaPageBitmap[%i] = 0x%08x", idx, gaPageBitmap[idx]);
336         
337         LOG("idx = %i, sidx = %i", idx, sidx);
338         #else
339         
340         #endif
341         
342         // Check if the gap is large enough
343         while( idx >= 0 )
344         {
345                 // Find a free page
346                 for( ; ; )
347                 {
348                         // Bulk Skip
349                         if( gaPageBitmap[idx] == -1 ) {
350                                 idx --;
351                                 sidx = 31;
352                                 continue;
353                         }
354                         
355                         if( gaPageBitmap[idx] & (1 << sidx) ) {
356                                 sidx --;
357                                 if(sidx < 0) {  sidx = 31;      idx --; }
358                                 if(idx < 0)     break;
359                                 continue;
360                         }
361                         break;
362                 }
363                 if( idx < 0 )   break;
364                 
365                 // Check if it is a free range
366                 for( i = 0; i < Pages; i++ )
367                 {
368                         // Used page? break
369                         if( gaPageBitmap[idx] & (1 << sidx) )
370                                 break;
371                         
372                         sidx --;
373                         if(sidx < 0) {  sidx = 31;      idx --; }
374                         if(idx < 0)     break;
375                 }
376                 
377                 if( i == Pages )
378                         break;
379         }
380         
381         // Check if an address was found
382         if( idx < 0 ) {
383                 Mutex_Release( &glPhysAlloc );
384                 Warning("MM_AllocPhysRange - OUT OF MEMORY (Called by %p)", __builtin_return_address(0));
385                 LEAVE('i', 0);
386                 return 0;
387         }
388         
389         // Mark pages used
390         for( i = 0; i < Pages; i++ )
391         {
392                 if(gaPageReferences)
393                         gaPageReferences[idx*32+sidx] = 1;
394                 gaPageBitmap[ idx ] |= 1 << sidx;
395                 sidx ++;
396                 giPhysAlloc ++;
397                 if(sidx == 32) { sidx = 0;      idx ++; }
398         }
399         
400         // Get address
401         ret = (idx << 17) | (sidx << 12);
402         
403         // Mark used block
404         if(gaPageBitmap[ idx ] == -1)   gaSuperBitmap[idx/32] |= 1 << (idx%32);
405
406         // Release Spinlock
407         Mutex_Release( &glPhysAlloc );
408         
409         LEAVE('X', ret);
410         #if TRACE_ALLOCS
411         Log_Debug("PMem", "MM_AllocPhysRange: RETURN 0x%llx-0x%llx (%i free)",
412                 ret, ret + (1<<Pages)-1, giPageCount-giPhysAlloc);
413         #endif
414         return ret;
415 }
416
417 /**
418  * \fn void MM_RefPhys(tPAddr PAddr)
419  */
420 void MM_RefPhys(tPAddr PAddr)
421 {
422         // Get page number
423         PAddr >>= 12;
424         
425         // We don't care about non-ram pages
426         if(PAddr >= giPageCount)        return;
427         
428         // Lock Structures
429         Mutex_Acquire( &glPhysAlloc );
430         
431         // Reference the page
432         if(gaPageReferences)
433                 gaPageReferences[ PAddr ] ++;
434         
435         // Mark as used
436         gaPageBitmap[ PAddr / 32 ] |= 1 << (PAddr&31);
437         
438         // Mark used block
439         if(gaPageBitmap[ PAddr / 32 ] == -1)
440                 gaSuperBitmap[PAddr/1024] |= 1 << ((PAddr/32)&31);
441         
442         // Release Spinlock
443         Mutex_Release( &glPhysAlloc );
444 }
445
446 /**
447  * \fn void MM_DerefPhys(tPAddr PAddr)
448  * \brief Dereferences a physical page
449  */
450 void MM_DerefPhys(tPAddr PAddr)
451 {
452         // Get page number
453         PAddr >>= 12;
454         
455         // We don't care about non-ram pages
456         if(PAddr >= giPageCount)        return;
457         
458         // Check if it is freed
459         if(gaPageReferences[ PAddr ] == 0) {
460                 Warning("MM_DerefPhys - Non-referenced memory dereferenced");
461                 return;
462         }
463         
464         // Lock Structures
465         Mutex_Acquire( &glPhysAlloc );
466         
467         if( giLastPossibleFree < PAddr )
468                 giLastPossibleFree = PAddr;
469
470         // Dereference
471         gaPageReferences[ PAddr ] --;
472         
473         // Mark as free in bitmaps
474         if( gaPageReferences[ PAddr ] == 0 )
475         {
476                 #if TRACE_ALLOCS
477                 Log_Debug("PMem", "MM_DerefPhys: Free'd 0x%x (%i free)", PAddr, giPageCount-giPhysAlloc);
478                 #endif
479                 //LOG("Freed 0x%x by %p\n", PAddr<<12, __builtin_return_address(0));
480                 giPhysAlloc --;
481                 gaPageBitmap[ PAddr / 32 ] &= ~(1 << (PAddr&31));
482                 if(gaPageReferences[ PAddr ] == 0)
483                         gaSuperBitmap[ PAddr >> 10 ] &= ~(1 << ((PAddr >> 5)&31));
484         }
485         
486         // Release spinlock
487         Mutex_Release( &glPhysAlloc );
488 }
489
490 /**
491  * \fn int MM_GetRefCount(tPAddr Addr)
492  */
493 int MM_GetRefCount(tPAddr PAddr)
494 {
495         // Get page number
496         PAddr >>= 12;
497         
498         // We don't care about non-ram pages
499         if(PAddr >= giPageCount)        return -1;
500         
501         // Check if it is freed
502         return gaPageReferences[ PAddr ];
503 }

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