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

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